Greasy Fork

Greasy Fork is available in English.

更好的 Greasy Fork

增强 Greasy Fork,在标题旁显示脚本图标,为编辑器(评论和描述)添加 Markdown 格式工具栏,可在代码页面直接下载 .user.js 文件,并改进页面设计。

当前为 2025-10-16 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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.0
// @description         Enhances Greasy Fork by displaying the script icon next to the title, adding a Markdown formatting toolbar to the editor (for comments and descriptions), allowing the script to be downloaded as a .user.js file directly from the code page, and improving the overall page design.
// @description:pt-BR   Aprimora o Greasy Fork exibindo o ícone do script ao lado do título, adiciona uma barra de formatação Markdown ao editor (para comentários e descrições), permite baixar o script como um arquivo .user.js diretamente na página de código e traz outras melhorias ao design da página.
// @description:zh-CN   增强 Greasy Fork,在标题旁显示脚本图标,为编辑器(评论和描述)添加 Markdown 格式工具栏,可在代码页面直接下载 .user.js 文件,并改进页面设计。
// @description:zh-TW   增強 Greasy Fork,在標題旁顯示腳本圖示,為編輯器(評論與描述)新增 Markdown 格式工具列,可在程式碼頁面直接下載 .user.js 檔案,並改進頁面設計。
// @description:en      Enhances Greasy Fork by displaying the script icon next to the title, adding a Markdown formatting toolbar to the editor (for comments and descriptions), allowing the script to be downloaded as a .user.js file directly from the code page, and improving the overall page design.
// @description:es      Mejora Greasy Fork mostrando el icono del script junto al título, añade una barra de formato Markdown al editor (para comentarios y descripciones), permite descargar el script como archivo .user.js directamente desde la página de código y mejora el diseño general de la página.
// @description:ja      Greasy Fork を強化し、タイトルの横にスクリプトアイコンを表示、エディター(コメントと説明)に Markdown 書式ツールバーを追加し、コードページから直接 .user.js ファイルとしてスクリプトをダウンロードできるようにし、ページ全体のデザインを改善します。
// @description:ko      Greasy Fork를 개선하여 제목 옆에 스크립트 아이콘을 표시하고, 편집기(댓글 및 설명)에 Markdown 서식 도구 모음을 추가하며, 코드 페이지에서 .user.js 파일로 직접 스크립트를 다운로드할 수 있게 하고, 페이지 디자인 전반을 개선합니다.
// @description:de      Verbessert Greasy Fork, indem das Skriptsymbol neben dem Titel angezeigt wird, fügt dem Editor (für Kommentare und Beschreibungen) eine Markdown-Formatierungsleiste hinzu, ermöglicht das direkte Herunterladen des Skripts als .user.js-Datei von der Code-Seite und verbessert das gesamte Seitendesign.
// @description:fr      Améliore Greasy Fork en affichant l’icône du script à côté du titre, ajoute une barre de mise en forme Markdown à l’éditeur (pour les commentaires et descriptions), permet de télécharger le script au format .user.js directement depuis la page du code, et améliore le design général de la page.
// @author              OHAS
// @license             CC-BY-NC-ND-4.0
// @copyright           2025 OHAS. All Rights Reserved. (https://gist.github.com/0H4S/ae2fa82957a089576367e364cbf02438)
// @icon                https://gist.githubusercontent.com/0H4S/0f1517e84b8f0aaa2d1f26e853096f62/raw/BGF_icon.svg
// @match               http://greasyfork.icu/*
// @require             https://update.greasyfork.icu/scripts/549920/Script%20Notifier.js
// @connect             update.greasyfork.org
// @connect             gist.githubusercontent.com
// @grant               GM_xmlhttpRequest
// @grant               GM_addStyle
// @grant               GM_getValue
// @grant               GM_setValue
// @grant               GM_registerMenuCommand
// @compatible          chrome
// @compatible          firefox
// @compatible          edge
// @run-at              document-idle
// ==/UserScript==

(function () {
    'use strict';
    if (window.top !== window.self) {
        return;
    }
    const SCRIPT_CONFIG = {
        notificationsUrl: 'https://gist.githubusercontent.com/0H4S/1eee8eb439b554860274686143eda3f9/raw/better_greasy_fork.notifications.json',
        scriptVersion: '1.0',
    };
    const notifier = new ScriptNotifier(SCRIPT_CONFIG);
    notifier.run();

    // ================
    // SESSÃO ÍCONES
    // ================

    const CACHE_KEY = 'Icons';
    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 extractIconFromContent(content) {
        if (typeof content !== 'string') return null;
        const match = content.match(/\/\/\s*@icon\s+(.+)/i);
        return match ? match[1].trim() : null;
    }

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

    async function processScript(normalizedPath, targetElement, isHeader = false) {
        if (processedKeys.has(normalizedPath)) {
            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();
        if (cached && now - cached.ts < 30 * 24 * 60 * 60 * 1000 && isValidIconUrl(cached.iconUrl)) {
            targetElement.prepend(createIconElement(cached.iconUrl, isHeader));
            return;
        }
        const scriptId = extractScriptIdFromNormalizedPath(normalizedPath);
        if (!scriptId) {
            iconCache[normalizedPath] = {
                iconUrl: null,
                ts: now
            };
            await saveCache();
            return;
        }
        const scriptUrl = `https://update.greasyfork.icu/scripts/${scriptId}.js`;
        GM_xmlhttpRequest({
            method: 'GET',
            url: scriptUrl,
            timeout: 6000,
            onload: async function (res) {
                if (typeof res.responseText !== 'string') {
                    iconCache[normalizedPath] = {
                        iconUrl: null,
                        ts: now
                    };
                    await saveCache();
                    return;
                }
                const iconUrl = extractIconFromContent(res.responseText);
                if (isValidIconUrl(iconUrl)) {
                    iconCache[normalizedPath] = {
                        iconUrl,
                        ts: now
                    };
                    await saveCache();
                    targetElement.prepend(createIconElement(iconUrl, isHeader));
                } else {
                    iconCache[normalizedPath] = {
                        iconUrl: null,
                        ts: now
                    };
                    await saveCache();
                }
            },
            onerror: async function () {
                iconCache[normalizedPath] = {
                    iconUrl: null,
                    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();
    }

    // ================
    // SESSÃO GLOBAL
    // ================

    const translations = {
        'en': { langName: 'English', languageSettings: '🌐 Language', close: 'Close', 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', list_item_placeholder: 'Item', ordered_list: 'Ordered List', quote: 'Quote', quote_placeholder: 'Quote', inline_code: 'Inline Code', inline_code_placeholder: 'code', code_block: 'Code Block', code_block_placeholder: 'code here', horizontal_line: 'Horizontal Line', link: 'Link', prompt_insert_url: 'Enter the URL:', link_text_placeholder: 'link text', image: 'Image', prompt_insert_image_url: 'Enter the image URL (https):', table: 'Table', prompt_columns: 'Number of columns:', prompt_rows: 'Number of rows:', table_header_placeholder: 'Header', table_cell_placeholder: 'Cell', video: 'Video (YouTube/Bilibili)', prompt_insert_video_url: 'Enter the video URL (YouTube or Bilibili):', alert_invalid_video_url: 'Invalid or unsupported video URL.', 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', download: 'Download', 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.' },

        'pt-BR': { langName: 'Português (BR)', languageSettings: '🌐 Idioma', close: 'Fechar', 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', list_item_placeholder: 'Item', ordered_list: 'Lista ordenada', quote: 'Citação', quote_placeholder: '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', 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):', table: 'Tabela', prompt_columns: 'Número de colunas:', prompt_rows: 'Número de linhas:', table_header_placeholder: 'Cabeçalho', table_cell_placeholder: 'Célula', video: 'Vídeo (YouTube/Bilibili)', prompt_insert_video_url: 'Insira a URL do vídeo (YouTube ou Bilibili):', alert_invalid_video_url: 'URL de vídeo inválida ou não suportada.', 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', download: 'Baixar', 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.' },

        'es': { langName: 'Español', languageSettings: '🌐 Idioma', close: 'Cerrar', 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', list_item_placeholder: 'Elemento', ordered_list: 'Lista ordenada', quote: 'Cita', quote_placeholder: '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', 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):', table: 'Tabla', prompt_columns: 'Número de columnas:', prompt_rows: 'Número de filas:', table_header_placeholder: 'Encabezado', table_cell_placeholder: 'Celda', video: 'Video (YouTube/Bilibili)', prompt_insert_video_url: 'Introduzca la URL del video (YouTube o Bilibili):', alert_invalid_video_url: 'URL de video no válida o no compatible.', 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 abreviatura?', abbreviation_placeholder: 'HTML', text_color: 'Color del texto', colored_text_placeholder: 'texto coloreado', background_color: 'Color de fondo', colored_background_placeholder: 'fondo coloreado', download: 'Descargar', 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.' },

        'zh-CN': { langName: '简体中文', languageSettings: '🌐 语言', close: '关闭', titles: '标题', title_placeholder: '标题', bold: '粗体', bold_placeholder: '粗体文本', italic: '斜体', italic_placeholder: '斜体文本', underline: '下划线', underline_placeholder: '下划线文本', strikethrough: '删除线', strikethrough_placeholder: '删除线文本', unordered_list: '无序列表', list_item_placeholder: '项目', ordered_list: '有序列表', quote: '引用', quote_placeholder: '引用', inline_code: '行内代码', inline_code_placeholder: '代码', code_block: '代码块', code_block_placeholder: '在此处编写代码', horizontal_line: '水平线', link: '链接', prompt_insert_url: '请输入网址:', link_text_placeholder: '链接文本', image: '图片', prompt_insert_image_url: '请输入图片网址 (https):', table: '表格', prompt_columns: '列数:', prompt_rows: '行数:', table_header_placeholder: '标题', table_cell_placeholder: '单元格', video: '视频 (YouTube/Bilibili)', prompt_insert_video_url: '请输入视频网址 (YouTube or Bilibili):', alert_invalid_video_url: '无效或不支持的视频网址。', 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: '彩色背景', download: '下载', notFound: '未找到代码!', scriptIdNotFound: '无法识别脚本 ID。', downloading: '下载中...', downloadError: '下载脚本时发生错误。', downloadTimeout: '脚本下载超时。' }
    };

    let currentLang = 'en';
    let languageModal = null;
    const LANG_STORAGE_KEY = 'UserScriptLang';

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

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

    const icons = {
        h: `<svg viewBox="0 0 16 16"><path d="M3.75 2a.75.75 0 0 1 .75.75V7h7V2.75a.75.75 0 0 1 1.5 0v10.5a.75.75 0 0 1-1.5 0V8.5h-7v4.75a.75.75 0 0 1-1.5 0V2.75A.75.75 0 0 1 3.75 2Z"></path></svg>`,
        bold: `<svg viewBox="0 0 16 16"><path d="M4 2h4.5a3.5 3.5 0 0 1 2.85 5.53A3.5 3.5 0 0 1 9.5 14H4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Zm1 7v3h4.5a1.5 1.5 0 0 0 0-3Zm3.5-2a1.5 1.5 0 0 0 0-3H5v3Z"></path></svg>`,
        italic: `<svg viewBox="0 0 16 16"><path d="M6 2.75A.75.75 0 0 1 6.75 2h6.5a.75.75 0 0 1 0 1.5h-2.505l-3.858 9H9.25a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.505l3.858-9H6.75A.75.75 0 0 1 6 2.75Z"></path></svg>`,
        underline: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M11 1h2v6.5c0 2.485-2.239 4.5-5 4.5S3 9.985 3 7.5V1h2v6.5c0 .628.285 1.23.802 1.695C6.379 9.714 7.159 10 8 10s1.621-.286 2.198-.805C10.715 8.729 11 8.127 11 7.5V1zM3 13h10v2H3z"/></svg>`,
        strikethrough: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M952 474H569.9c-10-2-20.5-4-31.6-6c-15.9-2.9-22.2-4.1-30.8-5.8c-51.3-10-82.2-20-106.8-34.2c-35.1-20.5-52.2-48.3-52.2-85.1c0-37 15.2-67.7 44-89c28.4-21 68.8-32.1 116.8-32.1c54.8 0 97.1 14.4 125.8 42.8c14.6 14.4 25.3 32.1 31.8 52.6c1.3 4.1 2.8 10 4.3 17.8c.9 4.8 5.2 8.2 9.9 8.2h72.8c5.6 0 10.1-4.6 10.1-10.1v-1c-.7-6.8-1.3-12.1-2-16c-7.3-43.5-28-81.7-59.7-110.3c-44.4-40.5-109.7-61.8-188.7-61.8c-72.3 0-137.4 18.1-183.3 50.9c-25.6 18.4-45.4 41.2-58.6 67.7c-13.5 27.1-20.3 58.4-20.3 92.9c0 29.5 5.7 54.5 17.3 76.5c8.3 15.7 19.6 29.5 34.1 42H72c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h433.2c2.1.4 3.9.8 5.9 1.2c30.9 6.2 49.5 10.4 66.6 15.2c23 6.5 40.6 13.3 55.2 21.5c35.8 20.2 53.3 49.2 53.3 89c0 35.3-15.5 66.8-43.6 88.8c-30.5 23.9-75.6 36.4-130.5 36.4c-43.7 0-80.7-8.5-110.2-25c-29.1-16.3-49.1-39.8-59.7-69.5c-.8-2.2-1.7-5.2-2.7-9c-1.2-4.4-5.3-7.5-9.7-7.5h-79.7c-5.6 0-10.1 4.6-10.1 10.1v1c.2 2.3.4 4.2.6 5.7c6.5 48.8 30.3 88.8 70.7 118.8c47.1 34.8 113.4 53.2 191.8 53.2c84.2 0 154.8-19.8 204.2-57.3c25-18.9 44.2-42.2 57.1-69c13-27.1 19.7-57.9 19.7-91.5c0-31.8-5.8-58.4-17.8-81.4c-5.8-11.2-13.1-21.5-21.8-30.8H952c4.4 0 8-3.6 8-8v-60a8 8 0 0 0-8-7.9z"/></svg>`,
        link: `<svg viewBox="0 0 16 16"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .75.75 0 0 1 1.06-1.06 2 2 0 0 0 2.83 0l2.5-2.5a2 2 0 0 0-2.83-2.83l-1.25 1.25a.75.75 0 0 1-1.06-1.06Zm-4.69 9.64a2 2 0 0 0 2.83 0l1.25-1.25a.75.75 0 0 1 1.06 1.06l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .75.75 0 0 1-1.06 1.06 2 2 0 0 0-2.83 0l-2.5 2.5a2 2 0 0 0 0 2.83Z"></path></svg>`,
        quote: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 304 384"><path fill="currentColor" d="m21 299l43-86H0V85h128v128l-43 86H21zm171 0l43-86h-64V85h128v128l-43 86h-64z"/></svg>`,
        code: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="m.586 12l4.95-4.95L6.95 8.464L3.414 12l3.536 3.536l-1.414 1.414L.586 12Zm8.201 8.728l4.486-17.94l1.94.485l-4.485 17.94l-1.94-.485Zm8.263-5.192L20.586 12L17.05 8.464l1.415-1.414l4.95 4.95l-4.95 4.95l-1.415-1.414Z"/></svg>`,
        code_block: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path id="oouiCode0" fill="currentColor" d="M1 10.08V8.92h1.15c1.15 0 1.15 0 1.15-1.15V5a7.42 7.42 0 0 1 .09-1.3a2 2 0 0 1 .3-.7a1.84 1.84 0 0 1 .93-.68A6.44 6.44 0 0 1 6.74 2h1.18v1.15h-.86A1.32 1.32 0 0 0 6 3.62a1.71 1.71 0 0 0-.36 1.23V7a3.22 3.22 0 0 1-.28 1.72a2 2 0 0 1-1.26.77a2.15 2.15 0 0 1 1.26.79A3.26 3.26 0 0 1 5.62 12v3.15A1.67 1.67 0 0 0 6 16.37a1.31 1.31 0 0 0 1.08.47h.87V18H6.74a6.3 6.3 0 0 1-2.12-.29a1.82 1.82 0 0 1-.93-.71a1.94 1.94 0 0 1-.3-.72A7.46 7.46 0 0 1 3.31 15v-3.77c0-1.15 0-1.15-1.15-1.15zm18 0V8.92h-1.15c-1.15 0-1.15 0-1.15-1.15V5a7.42 7.42 0 0 0-.08-1.32a2 2 0 0 0-.3-.73a1.84 1.84 0 0 0-.93-.68A6.44 6.44 0 0 0 13.26 2h-1.18v1.15h.87a1.32 1.32 0 0 1 1.05.47a1.71 1.71 0 0 1 .36 1.23V7a3.22 3.22 0 0 0 .28 1.72a2 2 0 0 0 1.26.77a2.15 2.15 0 0 0-1.26.79a3.26 3.26 0 0 0-.26 1.72v3.15a1.67 1.67 0 0 1-.38 1.22a1.31 1.31 0 0 1-1.08.47h-.87V18h1.19a6.3 6.3 0 0 0 2.12-.29a1.82 1.82 0 0 0 .93-.68a1.94 1.94 0 0 0 .3-.72a7.46 7.46 0 0 0 .1-1.31v-3.77c0-1.15 0-1.15 1.15-1.15z"/><use href="#oouiCode0" transform="matrix(-1 0 0 1 20 0)"/></svg>`,
        ul: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1025 1024"><path fill="currentColor" d="M960.356 1024h-512q-27 0-45.5-19t-18.5-45V832q0-26 18.5-45t45.5-19h512q26 0 45 19t19 45v128q0 27-19 45.5t-45 18.5zm0-384h-512q-27 0-45.5-18.5t-18.5-45.5V448q0-27 18.5-45.5t45.5-18.5h512q26 0 45 18.5t19 45.5v128q0 27-19 45.5t-45 18.5zm0-384h-512q-27 0-45.5-19t-18.5-45V64q0-27 18.5-45.5t45.5-18.5h512q26 0 45 18.5t19 45.5v128q0 26-19 45t-45 19zm-768 768h-128q-26 0-45-19t-19-45V832q0-26 19-45t45-19h128q26 0 45 19t19 45v128q0 27-18.5 45.5t-45.5 18.5zm0-384h-128q-26 0-45-18.5t-19-45.5V448q0-27 19-45.5t45-18.5h128q26 0 45 18.5t19 45.5v128q0 27-18.5 45.5t-45.5 18.5zm0-384h-128q-26 0-45-19t-19-45V64q0-27 19-45.5t45-18.5h128q26 0 45 18.5t19 45.5v128q0 26-18.5 45t-45.5 19z"/></svg>`,
        ol: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M3.684 1.01c.193.045.33.21.33.402v3.294a.42.42 0 0 1-.428.412a.42.42 0 0 1-.428-.412V2.58a3.11 3.11 0 0 1-.664.435a.436.436 0 0 1-.574-.184a.405.405 0 0 1 .192-.552c.353-.17.629-.432.82-.661a2.884 2.884 0 0 0 .27-.388a.44.44 0 0 1 .482-.22Zm-1.53 6.046a.401.401 0 0 1 0-.582l.002-.001V6.47l.004-.002l.008-.008a1.12 1.12 0 0 1 .103-.084a2.2 2.2 0 0 1 1.313-.435h.007c.32.004.668.084.947.283c.295.21.485.536.485.951c0 .452-.207.767-.488.992c-.214.173-.49.303-.714.409c-.036.016-.07.033-.103.049c-.267.128-.468.24-.61.39a.763.763 0 0 0-.147.22h1.635a.42.42 0 0 1 .427.411a.42.42 0 0 1-.428.412H2.457a.42.42 0 0 1-.428-.412c0-.51.17-.893.446-1.184c.259-.275.592-.445.86-.574c.043-.02.085-.04.124-.06c.231-.11.4-.19.529-.293c.12-.097.18-.193.18-.36c0-.148-.057-.23-.14-.289a.816.816 0 0 0-.448-.122a1.32 1.32 0 0 0-.818.289l-.005.005a.44.44 0 0 1-.602-.003Zm.94 5.885a.42.42 0 0 1 .427-.412c.294 0 .456-.08.537-.15a.303.303 0 0 0 .11-.246c-.006-.16-.158-.427-.647-.427c-.352 0-.535.084-.618.137a.349.349 0 0 0-.076.062l-.003.004a.435.435 0 0 0 .01-.018v.001l-.002.002l-.002.004l-.003.006l-.005.008l.002-.003a.436.436 0 0 1-.563.165a.405.405 0 0 1-.191-.552v-.002l.002-.003l.003-.006l.008-.013a.71.71 0 0 1 .087-.12c.058-.067.142-.146.259-.22c.238-.153.59-.276 1.092-.276c.88 0 1.477.556 1.502 1.22c.012.303-.1.606-.339.84c.238.232.351.535.34.838c-.026.664-.622 1.22-1.503 1.22c-.502 0-.854-.122-1.092-.275a1.19 1.19 0 0 1-.326-.308a.71.71 0 0 1-.02-.033l-.008-.013l-.003-.005l-.001-.003v-.001l-.001-.001a.405.405 0 0 1 .19-.553a.436.436 0 0 1 .564.165l.003.004c.01.01.033.035.076.063c.083.053.266.137.618.137c.489 0 .641-.268.648-.428a.303.303 0 0 0-.11-.245c-.082-.072-.244-.151-.538-.151a.42.42 0 0 1-.427-.412ZM7.75 3a.75.75 0 0 0 0 1.5h5.5a.75.75 0 0 0 0-1.5h-5.5Zm0 4a.75.75 0 0 0 0 1.5h5.5a.75.75 0 0 0 0-1.5h-5.5Zm0 4a.75.75 0 0 0 0 1.5h5.5a.75.75 0 0 0 0-1.5h-5.5Z"/></svg>`,
        image: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42 42"><path fill="currentColor" d="M.5 7.5v27c0 2.52.51 3 3 3h34c2.471 0 3-.46 3-3v-27c0-2.46-.471-3-3-3h-34c-2.48 0-3 .43-3 3zm35.29 23H5.23c3.34-4.87 9.279-12.99 10.789-12.99c1.461 0 6.42 6.561 8.661 8.87c0 0 2.881-3.851 4.391-3.851c1.538 0 6.669 7.931 6.719 7.971zm-8.979-17c0-2.04 1.649-3.689 3.689-3.689s3.689 1.649 3.689 3.689s-1.649 3.689-3.689 3.689s-3.689-1.649-3.689-3.689z"/></svg>`,
        video: `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 1024 768"><path fill="currentColor" d="M928 736q-222 32-416 32q-86 0-190-8t-165-16l-61-8q-27-5-47.5-37.5t-30-78.5t-14-86T0 461V307Q0 52 96 32Q318 0 512 0q86 0 190 8t165 16l61 8q29 4 49.5 36.5T1007 148t13 86t4 73v154q0 36-3 73t-12 85t-30 80t-51 37zM693 359L431 199q-11-10-29-5.5T384 208v352q0 11 18 15t29-6l262-160q11-10 11-25t-11-25z"/></svg>`,
        subscript: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 64C14.3 64 0 78.3 0 96s14.3 32 32 32h15.3l89.6 128l-89.6 128H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h32c10.4 0 20.2-5.1 26.2-13.6L176 311.8l85.8 122.6c6 8.6 15.8 13.6 26.2 13.6h32c17.7 0 32-14.3 32-32s-14.3-32-32-32h-15.3l-89.6-128l89.6-128H320c17.7 0 32-14.3 32-32s-14.3-32-32-32h-32c-10.4 0-20.2 5.1-26.2 13.6L176 200.2L90.2 77.6C84.2 69.1 74.4 64 64 64H32zm448 256c0-11.1-5.7-21.4-15.2-27.2s-21.2-6.4-31.1-1.4l-32 16c-15.8 7.9-22.2 27.1-14.3 42.9C393 361.5 404.3 368 416 368v80c-17.7 0-32 14.3-32 32s14.3 32 32 32h64c17.7 0 32-14.3 32-32s-14.3-32-32-32V320z"/></svg>`,
        superscript: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M480 32c0-11.1-5.7-21.4-15.2-27.2s-21.2-6.4-31.1-1.4l-32 16c-15.8 7.9-22.2 27.1-14.3 42.9C393 73.5 404.3 80 416 80v80c-17.7 0-32 14.3-32 32s14.3 32 32 32h64c17.7 0 32-14.3 32-32s-14.3-32-32-32V32zM32 64C14.3 64 0 78.3 0 96s14.3 32 32 32h15.3l89.6 128l-89.6 128H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h32c10.4 0 20.2-5.1 26.2-13.6L176 311.8l85.8 122.6c6 8.6 15.8 13.6 26.2 13.6h32c17.7 0 32-14.3 32-32s-14.3-32-32-32h-15.3l-89.6-128l89.6-128H320c17.7 0 32-14.3 32-32s-14.3-32-32-32h-32c-10.4 0-20.2 5.1-26.2 13.6L176 200.2L90.2 77.6C84.2 69.1 74.4 64 64 64H32z"/></svg>`,
        highlight: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="currentColor" d="M16.5 3v4a.5.5 0 0 1-.5.5H4a.5.5 0 0 1-.5-.5V3h13Zm-10 7.5v7l6.447-3.106a1 1 0 0 0 .553-.894v-3h-7Z"/></svg>`,
        abbreviation: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512"><path fill="currentColor" d="M20 424.229h20V279.771H20c-11.046 0-20-8.954-20-20V212c0-11.046 8.954-20 20-20h112c11.046 0 20 8.954 20 20v212.229h20c11.046 0 20 8.954 20 20V492c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20v-47.771c0-11.046 8.954-20 20-20zM96 0C56.235 0 24 32.235 24 72s32.235 72 72 72s72-32.235 72-72S135.764 0 96 0z"/></svg>`,
        keyboard: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" fill-rule="evenodd" d="M3 4h10a1.5 1.5 0 0 1 1.5 1.5v5A1.5 1.5 0 0 1 13 12H3a1.5 1.5 0 0 1-1.5-1.5v-5A1.5 1.5 0 0 1 3 4M0 5.5a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3zm6.25 3.25a.75.75 0 0 0 0 1.5h3.5a.75.75 0 0 0 0-1.5zM4.5 6.5a1 1 0 1 1-2 0a1 1 0 0 1 2 0m2 1a1 1 0 1 0 0-2a1 1 0 0 0 0 2m4-1a1 1 0 1 1-2 0a1 1 0 0 1 2 0m2 1a1 1 0 1 0 0-2a1 1 0 0 0 0 2m-8 2a1 1 0 1 1-2 0a1 1 0 0 1 2 0m8 1a1 1 0 1 0 0-2a1 1 0 0 0 0 2" clip-rule="evenodd"/></svg>`,
        text_color: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="currentColor" d="M12 1v2a1 1 0 0 1-2 0V2H7v8h1a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2h1V2H2v1a1 1 0 1 1-2 0V1a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1Z"/></svg>`,
        background_color: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M4 20q-.825 0-1.413-.588T2 18V6q0-.825.588-1.413T4 4h16q.825 0 1.413.588T22 6v12q0 .825-.588 1.413T20 20H4Zm2-3h12q.425 0 .713-.288T19 16q0-.425-.288-.713T18 15H6q-.425 0-.713.288T5 16q0 .425.288.713T6 17Zm0-4h12q.425 0 .713-.288T19 12q0-.425-.288-.713T18 11H6q-.425 0-.713.288T5 12q0 .425.288.713T6 13Zm0-4h8q.425 0 .713-.288T15 8q0-.425-.288-.713T14 7H6q-.425 0-.713.288T5 8q0 .425.288.713T6 9Z"/></svg>`,
        table: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="currentColor" d="M2 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zm0 4h7v4H2zm0 10v-4h7v4zm16 0h-7v-4h7zm0-6h-7V6h7z"/></svg>`,
        hr: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M2 7.75A.75.75 0 0 1 2.75 7h10.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 7.75Z"/></svg>`,
    };

    GM_addStyle(`
        .txt-editor-container.dark-theme { background-color: #0d1117; border: 1px solid #3d444d; }
        .dark-theme .txt-editor-toolbar { background-color: #151b23f2; border-bottom: 1px solid #3d444d; }
        .dark-theme .txt-editor-toolbar-button, .dark-theme .txt-editor-toolbar-select { color: #9198a1; }
        .dark-theme .txt-editor-toolbar-button:hover, .dark-theme .txt-editor-toolbar-select:hover { background-color: #212830; color: #fcf0f0ff; }
        .dark-theme .txt-editor-toolbar-button[data-tooltip]:hover::after { background-color: #212830; color: #fcf0f7ff; }
        .dark-theme textarea { background-color: #0d1117; color: #f0f6fc; border-top: 1px solid #3d444d; }
        .dark-theme .txt-editor-toolbar-divider { border-left: 1px solid #3d444d; }
        .dark-theme .txt-editor-toolbar-select { background-color: #151b23; background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="%239198a1" viewBox="0 0 16 16"><path d="M4.427 7.427a.25.25 0 0 0-.354.354l4 4a.25.25 0 0 0 .354 0l4-4a.25.25 0 0 0-.354-.354L8.25 11.22l-3.823-3.793Z"></path></svg>'); }
        .dark-theme .txt-editor-toolbar-select option { background: #0d1117; color: #f0f6fc; }
        .dark-theme .txt-color-picker-input { border: 1px solid #3d444d; background-color: #0d1117; }
        .txt-editor-container.light-theme { background-color: #ffffff; border: 1px solid #d0d7de; }
        .light-theme .txt-editor-toolbar { background-color: #f6f8fa; border-bottom: 1px solid #d0d7de; }
        .light-theme .txt-editor-toolbar-button, .light-theme .txt-editor-toolbar-select { color: #57606a; }
        .light-theme .txt-editor-toolbar-button:hover, .light-theme .txt-editor-toolbar-select:hover { background-color: #ebecf0; color: #24292f; }
        .light-theme .txt-editor-toolbar-button[data-tooltip]:hover::after { background-color: #24292f; color: #ffffff; }
        .light-theme textarea { background-color: #ffffff; color: #24292f; border-top: 1px solid #d0d7de; }
        .light-theme .txt-editor-toolbar-divider { border-left: 1px solid #d0d7de; }
        .light-theme .txt-editor-toolbar-select { background-color: #f6f8fa; background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="%2357606a" viewBox="0 0 16 16"><path d="M4.427 7.427a.25.25 0 0 0-.354.354l4 4a.25.25 0 0 0 .354 0l4-4a.25.25 0 0 0-.354-.354L8.25 11.22l-3.823-3.793Z"></path></svg>'); }
        .light-theme .txt-editor-toolbar-select option { background: #ffffff; color: #24292f; }
        .light-theme .txt-color-picker-input { border: 1px solid #d0d7de; background-color: #ffffff; }
        .txt-editor-container { border-radius: 6px; margin: 10px 0; }
        .txt-editor-container textarea { border: 0; border-radius: 0 0 6px 6px; width: 100% !important; min-height: 180px; padding: 10px; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; resize: vertical; }
        .txt-editor-toolbar { display: flex; flex-wrap: wrap; align-items: center; padding: 8px 5px; }
        .txt-editor-toolbar-button, .txt-editor-toolbar-select { background: none; border: none; cursor: pointer; padding: 6px; margin: 0 2px; border-radius: 6px; position: relative; }
        .txt-editor-toolbar-button svg { width: 16px; height: 16px; fill: currentColor; vertical-align: middle; }
        .txt-editor-toolbar-button[data-tooltip]:hover::after { content: attr(data-tooltip); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); padding: 4px 8px; border-radius: 4px; font-size: 12px; white-space: nowrap; z-index: 10; margin-bottom: 5px; }
        .txt-editor-toolbar-divider { margin: 4px 8px; height: 16px; }
        .txt-editor-toolbar-select { -webkit-appearance: none; appearance: none; padding-right: 20px; background-repeat: no-repeat; background-position: right 6px center; }
        .txt-color-picker-container { display: flex; align-items: center; }
        .txt-color-picker-input { border-radius: 4px; width: 24px; height: 24px; padding: 1px; cursor: pointer; }
		.lang-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); display: none; z-index: 2147483647; justify-content: center; align-items: center; backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); }
		.lang-modal-box { padding: 24px; border-radius: 16px; width: min(90vw, 320px); text-align: center; transform: scale(0.95); opacity: 0; transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s ease-out; }
		.lang-modal-box.dark-theme { background-color: rgba(30, 30, 32, 0.85); border: 1px solid #333; box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); }
		.lang-modal-box.light-theme { background-color: rgba(255, 255, 255, 0.8); border: 1px solid rgba(0, 0, 0, 0.1); box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1); }
		.lang-modal-buttons { display: flex; flex-direction: column; gap: 12px; }
		.lang-modal-buttons button { padding: 14px; border-radius: 10px; font-weight: 500; cursor: pointer; transition: all 0.2s ease-in-out; text-align: center; font-size: 1rem; }
		.lang-modal-box.dark-theme .lang-modal-buttons button { background-color: #2a2d31; color: #e0e0e0; border: 1px solid #444; }
		.lang-modal-box.light-theme .lang-modal-buttons button { background-color: #f0f2f5; color: #333; border: 1px solid #ddd; }
		.lang-modal-box.dark-theme .lang-modal-buttons button:hover { border-color: #58a6ff; background-color: #313438; }
		.lang-modal-box.light-theme .lang-modal-buttons button:hover { border-color: #0969da; background-color: #e6e8eb; }
        .script-description-blockquote { margin: 15px 0 !important; padding: 12px 15px; border-radius: 6px; transition: background-color 0.2s, border-color 0.2s, color 0.2s; background-color: #e0e0e0ff; border-left: 4px solid #670000; color: #131313ff; }
        .script-description-blockquote * { font-weight: 600 !important; color: inherit !important; }
        @media (prefers-color-scheme: dark) { .script-description-blockquote { background-color: #1c2128; border-left: 4px solid #e95757; color: #ffffffff; } }
        .good-rating-count, .ok-rating-count, .bad-rating-count { font-weight: bold; display: inline-flex; align-items: center; font-size: 1.15em; padding: 2px 3px; border-radius: 5px; transition: background-color 0.2s ease; }
        .good-rating-count { color: #1f883d; }
        .ok-rating-count { color: #6e7781; }
        .bad-rating-count { color: #cf222e; }
        .good-rating-count:hover { background-color: rgba(31, 136, 61, 0.1); }
        .ok-rating-count:hover { background-color: rgba(110, 119, 129, 0.1); }
        .bad-rating-count:hover { background-color: rgba(207, 34, 46, 0.1); }
        .good-rating-count::before { content: '👍'; margin-right: 3px; }
        .ok-rating-count::before { content: '🤔'; margin-right: 3px; }
        .bad-rating-count::before { content: '👎'; margin-right: 3px; }
        @media (prefers-color-scheme: dark) { .ok-rating-count { color: #ccbf1c; }
        .good-rating-count:hover { background-color: rgba(46, 160, 67, 0.15); }
        .ok-rating-count:hover { background-color: rgba(139, 148, 158, 0.15); }
        .bad-rating-count:hover { background-color: rgba(248, 81, 73, 0.15); } }
        .script-list-ratings { align-self: center; }
        .script-list-ratings + dd { align-self: center; white-space: nowrap; }
        .bgs-info-separator { height: 3px; border: none; background-color: #b1b8c0; margin: 1.5em 0; }
        @media (prefers-color-scheme: dark) { .bgs-info-separator { background-color: #4e5761; } }
    `);

    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;
    }

    // ================
    // SESSÃO 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 => {
            if (element && element.parentElement.tagName !== 'BLOCKQUOTE') {
                const blockquoteWrapper = document.createElement('blockquote');
                blockquoteWrapper.className = 'script-description-blockquote';
                element.parentNode.insertBefore(blockquoteWrapper, element);
                blockquoteWrapper.appendChild(element);
            }
        });
    }

    // ================
    // SESSÃO EDITOR MD
    // ================

    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;
        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 createTextStyleEditor(textarea) {
        if (textarea.dataset.editorApplied) return;
        textarea.dataset.editorApplied = 'true';
        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': '# ', 'H2': '## ', 'H3': '### ', 'H4': '#### ', 'H5': '##### ', 'H6': '###### ' }, action: (val) => insertText(textarea, val, '', getTranslation('title_placeholder')) },
            { type: 'divider' },
            { title: getTranslation('bold'), icon: icons.bold, action: () => insertText(textarea, '**', '**', getTranslation('bold_placeholder')) },
            { title: getTranslation('italic'), icon: icons.italic, action: () => insertText(textarea, '*', '*', 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, '~~', '~~', getTranslation('strikethrough_placeholder')) },
            { type: 'divider' },
            { title: getTranslation('unordered_list'), icon: icons.ul, action: () => { const start = textarea.selectionStart, end = textarea.selectionEnd, selection = textarea.value.substring(start, end); textarea.setRangeText(selection ? selection.split('\n').map(line => line.trim() === '' ? '' : '- ' + line).join('\n') : '\n- ' + getTranslation('list_item_placeholder'), start, end, 'select'); textarea.focus(); } },
            { title: getTranslation('ordered_list'), icon: icons.ol, action: () => { const start = textarea.selectionStart, end = textarea.selectionEnd, selection = textarea.value.substring(start, end); if (selection) { let counter = 1; textarea.setRangeText(selection.split('\n').map(line => line.trim() === '' ? '' : (counter++) + '. ' + line).join('\n'), start, end, 'select'); } else insertText(textarea, '\n1. ', '', getTranslation('list_item_placeholder')); textarea.focus(); } },
            { type: 'divider' },
            { title: getTranslation('quote'), icon: icons.quote, action: () => insertText(textarea, '\n> ', '', getTranslation('quote_placeholder')) },
            { title: getTranslation('inline_code'), icon: icons.code, action: () => insertText(textarea, '`', '`', getTranslation('inline_code_placeholder')) },
            { title: getTranslation('code_block'), label: icons.code_block, action: () => insertText(textarea, '\n```\n', '\n```\n', getTranslation('code_block_placeholder')) },
            { title: getTranslation('horizontal_line'), icon: icons.hr, action: () => insertText(textarea, '\n---\n') },
            { type: 'divider' },
            { title: getTranslation('link'), icon: icons.link, action: () => { const url = prompt(getTranslation('prompt_insert_url'), "https://"); if (url) insertText(textarea, '[', `](${url})`, getTranslation('link_text_placeholder')); } },
            { title: getTranslation('image'), icon: icons.image, action: () => { const url = prompt(getTranslation('prompt_insert_image_url'), "https://"); if (url) insertText(textarea, `![alt text](${url})`); } },
            { title: getTranslation('table'), icon: icons.table, action: () => { const cols = parseInt(prompt(getTranslation('prompt_columns'), "3"), 10) || 3; const rows = parseInt(prompt(getTranslation('prompt_rows'), "2"), 10) || 2; let table = '\n| ' + Array(cols).fill(getTranslation('table_header_placeholder')).join(' | ') + ' |\n'; table += '| ' + Array(cols).fill('---').join(' | ') + ' |\n'; for (let i = 0; i < rows; i++) { table += '| ' + Array(cols).fill(getTranslation('table_cell_placeholder')).join(' | ') + ' |\n'; } insertText(textarea, table); } },
            { title: getTranslation('video'), icon: icons.video, action: () => { const url = prompt(getTranslation('prompt_insert_video_url')); if (!url) return; let src = ''; 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]}`; if (src) insertText(textarea, `<iframe src="${src}" allowfullscreen></iframe>`); else alert(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 title = prompt(getTranslation('prompt_abbreviation_meaning')); if (title) insertText(textarea, `<abbr title="${title}">`, `</abbr>`, getTranslation('abbreviation_placeholder')); } },
            { type: 'color-picker' }
        ];

        tools.forEach(tool => {
            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';
                input.value = "#58a6ff";
                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: () => insertText(textarea, `<span style="background-color: ${input.value};">`, '</span>', getTranslation('colored_background_placeholder'))
                });
                colorContainer.append(input, colorBtn, bgBtn);
                toolbar.appendChild(colorContainer);

            } else {
                toolbar.appendChild(createToolbarButton(tool));
            }
        });
        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));
    }

    // ================
    // SESSÃO 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.icu/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);
        });
    }

    // ================
    // SESSÃO INICIALIZAR
    // ================

    async function start() {
        iconCache = await GM_getValue(CACHE_KEY, {});
        await determineLanguage();
        languageModal = createLanguageModal();
        document.body.appendChild(languageModal);
        registerLanguageMenu();
        if (isMarkdownPage()) {
            applyToAllTextareas();
            enableSourceEditorCheckbox();
        }
        if (isCodePage()){
            initializeDownloadButton();
        }
        processIconElements();
        highlightScriptDescription();
        if (isScriptPage()) {
            addAdditionalInfoSeparator();
        }
        const observer = new MutationObserver(() => {
            processIconElements();
            highlightScriptDescription();
            if (isScriptPage()) {
                addAdditionalInfoSeparator();
            }

            if (isMarkdownPage()) {
                applyToAllTextareas();
            }
        });
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }
    start();
})();