SpicyChat - Chat Editor

Customize SpicyChat AI appearance: widths, heights, fonts, text alignment

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         SpicyChat - Chat Editor
// @name:ru      SpicyChat - Редактор чата
// @namespace    https://github.com/bot286mpn/SpicyChat-ChatEditor
// @version      1.0.0
// @description  Customize SpicyChat AI appearance: widths, heights, fonts, text alignment
// @description:ru Настройте внешний вид SpicyChat AI: ширины, высоты, шрифты, выравнивание текста
// @author       YourName
// @match        https://spicychat.ai/chat/*
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // Защита от повторного запуска
    if (document.getElementById('spicychat-editor-active')) return;
    document.body.insertAdjacentHTML('beforeend', '<div id="spicychat-editor-active" style="display:none;"></div>');

    let currentSettings = null;
    let styleElement = null;
    let isPanelOpen = false;
    let isScriptEnabled = true;
    let currentProfileName = null;
    let profiles = {};
    let hasUnsavedChanges = false;

    // ФИКСИРОВАННЫЕ ОРИГИНАЛЬНЫЕ ЗНАЧЕНИЯ
    const ORIGINAL_VALUES = {
        CHAT_WIDTH: 800,        // px
        INPUT_WIDTH: 800,       // px
        TEXTAREA_MIN_HEIGHT: 11, // px
        TEXTAREA_MAX_HEIGHT: 200, // px - максимальная высота textarea
        CONTAINER_HEIGHT: 44,   // px - высота всего контейнера
        FONT_SIZE: 100,         // %
        FONT_FAMILY: 'Inter, system-ui, sans-serif'
    };

    // Дефолтные настройки
    const DEFAULT_SETTINGS = {
        chatWidth: 100,
        inputWidth: 100,
        inputHeight: 100,
        messageAlign: 'left',
        fontSize: 100,
        fontFamily: ORIGINAL_VALUES.FONT_FAMILY
    };

    // Применить стили с правильными селекторами и выравниванием
    function applyStyles() {
        if (!currentSettings || !isScriptEnabled) {
            if (styleElement) {
                styleElement.textContent = '';
            }
            return;
        }

        let css = '';
        console.log('Applying styles with settings:', currentSettings);

        // Ширина чата - расчет от фиксированного значения
        const newChatWidth = (ORIGINAL_VALUES.CHAT_WIDTH * currentSettings.chatWidth) / 100;

        css += `.w-full.flex.mb-lg.bg-transparent[style*="max-width: 800px"] {
            max-width: ${newChatWidth}px !important;
        }\n`;

        css += `.mb-lg.bg-transparent > div.flex.flex-col.gap-md.w-full {
            max-width: 100% !important;
            width: 100% !important;
        }\n`;

        // Ширина поля ввода - расчет от фиксированного значения
        const newInputWidth = (ORIGINAL_VALUES.INPUT_WIDTH * currentSettings.inputWidth) / 100;

        css += `.flex.justify-undefined.items-undefined.bg-transparent.w-full.right-0.pb-md.z-\\[1\\][style*="max-width: 800px"] {
            max-width: ${newInputWidth}px !important;
        }\n`;

        // ВЫСОТА ПОЛЯ ВВОДА - ТЕПЕРЬ В ПРОЦЕНТАХ
        if (currentSettings.inputHeight !== 100) {
            const heightMultiplier = currentSettings.inputHeight / 100;
            const newTextareaMinHeight = ORIGINAL_VALUES.TEXTAREA_MIN_HEIGHT * heightMultiplier;
            const newTextareaMaxHeight = ORIGINAL_VALUES.TEXTAREA_MAX_HEIGHT * heightMultiplier;
            const newContainerHeight = ORIGINAL_VALUES.CONTAINER_HEIGHT * heightMultiplier;

            // Основной textarea - ГЛАВНЫЙ ЭЛЕМЕНТ ДЛЯ ИЗМЕНЕНИЯ
            css += `textarea {
                min-height: ${newTextareaMinHeight}px !important;
                max-height: ${newTextareaMaxHeight}px !important;
                height: auto !important;
                align-self: flex-start !important;
                margin-top: 0 !important;
            }\n`;

            // Контейнер textarea (div с классами flex-grow max-h-[188px])
            css += `.flex-grow.max-h-\\[188px\\] {
                max-height: ${newTextareaMaxHeight}px !important;
                align-items: flex-start !important;
            }\n`;

            // Основной контейнер поля ввода
            css += `.w-full.border-1.border-solid.rounded-\\[13px\\].bg-gray-3 {
                min-height: ${newContainerHeight}px !important;
            }\n`;

            // Внутренний контейнер с кнопками - ИСПРАВЛЯЕМ ВЫРАВНИВАНИЕ
            css += `.flex.justify-between.items-end.py-sm.px-1.gap-0 {
                min-height: ${newContainerHeight - 16}px !important;
                align-items: flex-start !important;
            }\n`;

            // Контейнер с textarea и кнопками - ИСПРАВЛЯЕМ ВЫРАВНИВАНИЕ
            css += `.flex.flex-1.items-end.gap-1 {
                min-height: ${newTextareaMinHeight}px !important;
                align-items: flex-start !important;
            }\n`;

            // Дополнительные стили для правильного выравнивания
            css += `.flex.items-center.justify-center {
                align-items: flex-start !important;
            }\n`;

            // Исправляем выравнивание placeholder'а
            css += `textarea::placeholder {
                line-height: normal !important;
            }\n`;

        } else {
            // При 100% - возвращаем оригинальные стили
            css += `textarea {
                min-height: ${ORIGINAL_VALUES.TEXTAREA_MIN_HEIGHT}px !important;
                max-height: ${ORIGINAL_VALUES.TEXTAREA_MAX_HEIGHT}px !important;
                height: auto !important;
            }\n`;
            css += `.flex-grow.max-h-\\[188px\\] {
                max-height: ${ORIGINAL_VALUES.TEXTAREA_MAX_HEIGHT}px !important;
            }\n`;
            css += `.w-full.border-1.border-solid.rounded-\\[13px\\].bg-gray-3 {
                min-height: ${ORIGINAL_VALUES.CONTAINER_HEIGHT}px !important;
            }\n`;
        }

        // Стили для текста сообщений - ИСПРАВЛЕН КУРСИВ
        const textAlign = currentSettings.messageAlign === 'center' ? 'center' :
                         currentSettings.messageAlign === 'right' ? 'right' : 'left';

        // Базовый размер шрифта в пикселях (оригинальный размер)
        const baseFontSize = 16;

        // Рассчитываем новый размер шрифта
        const newFontSize = baseFontSize * (currentSettings.fontSize / 100);

        // ОБЩИЙ СТИЛЬ ДЛЯ ВСЕГО ТЕКСТА
        css += `div.mb-lg.bg-transparent span.leading-6,
                div.mb-lg.bg-transparent em,
                div.mb-lg.bg-transparent i,
                div.mb-lg.bg-transparent strong,
                div.mb-lg.bg-transparent b,
                div.mb-lg.bg-transparent u {
            font-size: ${newFontSize}px !important;
            font-family: ${currentSettings.fontFamily} !important;
            line-height: 1.5 !important;
        }\n`;

        // ДОПОЛНИТЕЛЬНЫЕ СТИЛИ ДЛЯ КУРСИВА - убираем все возможные влияния
        css += `div.mb-lg.bg-transparent em,
                div.mb-lg.bg-transparent i {
            font-style: italic !important;
            transform: none !important;
            scale: 1 !important;
            zoom: 1 !important;
            max-width: none !important;
            width: auto !important;
            transition: none !important;
            animation: none !important;
            display: inline !important;
            vertical-align: baseline !important;
        }\n`;

        // Специально для span внутри курсива (на случай вложенности)
        css += `div.mb-lg.bg-transparent em span,
                div.mb-lg.bg-transparent i span,
                div.mb-lg.bg-transparent em *,
                div.mb-lg.bg-transparent i * {
            font-size: inherit !important;
            transform: none !important;
            scale: 1 !important;
        }\n`;

        // Выравнивание текста
        css += `div.mb-lg.bg-transparent span.leading-6,
               div.mb-lg.bg-transparent em,
               div.mb-lg.bg-transparent i,
               div.mb-lg.bg-transparent strong,
               div.mb-lg.bg-transparent b,
               div.mb-lg.bg-transparent u {
            text-align: ${textAlign} !important;
        }\n`;

        // Удаление надписи
        css += `div.flex.flex-col.justify-center.items-center.gap-sm > p.text-label-md.font-regular.text-left.text-gray-11 {
            display: none !important;
        }\n`;

        if (!styleElement) {
            styleElement = document.createElement('style');
            styleElement.id = 'spicychat-custom-styles';
            document.head.appendChild(styleElement);
        }
        styleElement.textContent = css;
    }

    // === UI Функции ===
    const systemFonts = [
        ORIGINAL_VALUES.FONT_FAMILY,
        'Arial, sans-serif',
        'Verdana, sans-serif',
        'Tahoma, sans-serif',
        'Trebuchet MS, sans-serif',
        'Times New Roman, serif',
        'Georgia, serif',
        'Garamond, serif',
        'Courier New, monospace',
        'Impact, sans-serif',
        'Comic Sans MS, cursive',
        'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
    ];

    function saveProfiles() {
        try {
            GM_setValue('spicychat_profiles', JSON.stringify(profiles));
            GM_setValue('spicychat_current_profile', currentProfileName);
            GM_setValue('spicychat_script_enabled', isScriptEnabled);
        } catch (error) {
            console.error('SpicyChat Editor: Error saving profiles', error);
        }
    }

    function loadProfiles() {
        try {
            const savedProfiles = GM_getValue('spicychat_profiles', null);
            profiles = savedProfiles ? JSON.parse(savedProfiles) : {};

            // ИСПРАВЛЕНИЕ: проверяем и исправляем некорректные значения высоты
            Object.keys(profiles).forEach(profileName => {
                if (profiles[profileName].inputHeight < 100) {
                    profiles[profileName].inputHeight = 100;
                }
            });

            if (Object.keys(profiles).length === 0) {
                profiles['Оригинальные настройки'] = {...DEFAULT_SETTINGS};
                profiles['Мой профиль'] = {...DEFAULT_SETTINGS};
            }

            currentProfileName = GM_getValue('spicychat_current_profile', 'Профиль не выбран');
            isScriptEnabled = GM_getValue('spicychat_script_enabled', true);

            // Если выбран реальный профиль - загружаем его настройки
            if (currentProfileName !== 'Профиль не выбран' && profiles[currentProfileName]) {
                currentSettings = {...profiles[currentProfileName]};

                // Дополнительная проверка текущих настроек
                if (currentSettings.inputHeight < 100) {
                    currentSettings.inputHeight = 100;
                }
            } else {
                // Если "Профиль не выбран" - используем настройки по умолчанию
                currentSettings = {...DEFAULT_SETTINGS};
            }
        } catch (error) {
            console.error('SpicyChat Editor: Error loading profiles', error);
            profiles = {
                'Оригинальные настройки': {...DEFAULT_SETTINGS},
                'Мой профиль': {...DEFAULT_SETTINGS}
            };
            currentProfileName = 'Профиль не выбран';
            currentSettings = {...DEFAULT_SETTINGS};
            isScriptEnabled = true;
        }
    }

    function createButton() {
        const btn = document.createElement('div');
        btn.id = 'spicychat-editor-button';
        btn.innerHTML = isScriptEnabled ? '⚙️' : '🔴';
        btn.title = isScriptEnabled ? 'Настройки чата (включено)' : 'Настройки чата (выключено)';
        btn.style.cssText = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            width: 40px;
            height: 40px;
            background: ${isScriptEnabled ? '#3182ce' : '#e53e3e'};
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            cursor: pointer;
            z-index: 10000;
            font-size: 18px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
            transition: transform 0.2s, background 0.2s;
        `;

        btn.addEventListener('mouseenter', () => {
            btn.style.transform = 'scale(1.1)';
            btn.style.background = isScriptEnabled ? '#2c5282' : '#c53030';
        });
        btn.addEventListener('mouseleave', () => {
            btn.style.transform = 'scale(1)';
            btn.style.background = isScriptEnabled ? '#3182ce' : '#e53e3e';
        });
        btn.addEventListener('click', togglePanel);

        document.body.appendChild(btn);
        return btn;
    }

    function updateButton() {
        const btn = document.getElementById('spicychat-editor-button');
        if (btn) {
            btn.innerHTML = isScriptEnabled ? '⚙️' : '🔴';
            btn.title = isScriptEnabled ? 'Настройки чата (включено)' : 'Настройки чата (выключено)';
            btn.style.background = isScriptEnabled ? '#3182ce' : '#e53e3e';
        }
    }

    function createPanel() {
        const panel = document.createElement('div');
        panel.id = 'spicychat-editor-panel';
        panel.style.cssText = `
            position: fixed;
            top: 10px;
            right: 10px;
            background: #2d3748;
            padding: 15px;
            border-radius: 8px;
            z-index: 9999;
            color: white;
            width: 400px;
            display: none;
            font-family: sans-serif;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            max-height: 85vh;
            overflow-y: auto;
        `;

        const createControl = (key, name, min, max, unit='%') => {
            const value = currentSettings[key];
            return `
                <div style="margin-bottom:15px;">
                    <label style="display:block; margin-bottom:5px; font-weight:bold;">${name}:</label>
                    <div style="display:flex; align-items:center; gap:10px;">
                        <input type="range" id="slider-${key}" min="${min}" max="${max}" value="${value}" style="flex:1;">
                        <input type="number" id="input-${key}" value="${value}" min="${min}" max="${max}" style="width:70px;">
                        <span>${unit}</span>
                    </div>
                </div>
            `;
        };

        const createFontFamilyControl = () => {
            let options = systemFonts.map(font =>
                `<option value="${font}" ${currentSettings.fontFamily === font ? 'selected' : ''}>${font.split(',')[0]}</option>`
            ).join('');
            return `
                <div style="margin-bottom:15px;">
                    <label style="display:block; margin-bottom:5px; font-weight:bold;">Семейство шрифтов:</label>
                    <select id="font-family" style="width:100%;padding:8px;border-radius:4px;background:#4a5568;color:white;border:1px solid #718096;">
                        ${options}
                    </select>
                </div>
            `;
        };

        const createProfilesControl = () => {
            let options = '<option value="Профиль не выбран"' + (currentProfileName === 'Профиль не выбран' ? ' selected' : '') + '>Профиль не выбран</option>';
            options += Object.keys(profiles).map(name =>
                `<option value="${name}" ${currentProfileName === name ? 'selected' : ''}>${name}</option>`
            ).join('');

            return `
                <div style="margin-bottom:15px;">
                    <label style="display:block; margin-bottom:5px; font-weight:bold;">Профиль настроек:</label>
                    <div style="display:flex; gap:5px; margin-bottom:10px;">
                        <select id="profile-select" style="flex:1;padding:8px;border-radius:4px;background:#4a5568;color:white;border:1px solid #718096;">
                            ${options}
                        </select>
                        <button id="btn-delete-profile" style="padding:8px 12px;background:#e53e3e;color:white;border:none;border-radius:4px;cursor:pointer;" title="Удалить профиль" ${currentProfileName === 'Профиль не выбран' ? 'disabled' : ''}>🗑️</button>
                    </div>
                    <div style="display:flex; gap:5px;">
                        <input type="text" id="new-profile-name" placeholder="Название профиля" style="flex:1;padding:8px;border-radius:4px;background:#4a5568;color:white;border:1px solid #718096;">
                        <button id="btn-save-profile" style="padding:8px 12px;background:#48bb78;color:white;border:none;border-radius:4px;cursor:pointer;">💾</button>
                    </div>
                </div>
            `;
        };

        panel.innerHTML = `
            <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:15px;">
                <h3 style="margin:0;">Настройки чата</h3>
                <div style="display:flex; align-items:center; gap:10px;">
                    <label style="font-size:12px; color:#a0aec0;">Скрипт:</label>
                    <label class="switch">
                        <input type="checkbox" id="script-toggle" ${isScriptEnabled ? 'checked' : ''}>
                        <span class="slider round"></span>
                    </label>
                    <button id="btn-close" style="background:none; border:none; color:white; font-size:20px; cursor:pointer;">×</button>
                </div>
            </div>

            ${createProfilesControl()}

            <div style="margin-bottom:15px;">
                <label style="display:block; margin-bottom:5px; font-weight:bold;">Ширина чата:</label>
                <div style="display:flex; align-items:center; gap:10px;">
                    <input type="range" id="slider-chatWidth" min="50" max="200" value="${currentSettings.chatWidth}" style="flex:1;">
                    <input type="number" id="input-chatWidth" value="${currentSettings.chatWidth}" min="50" max="200" style="width:70px;">
                    <span>%</span>
                </div>
                <small style="color:#a0aec0; font-size:12px;">Оригинал: ${ORIGINAL_VALUES.CHAT_WIDTH}px (100%)</small>
            </div>

            <div style="margin-bottom:15px;">
                <label style="display:block; margin-bottom:5px; font-weight:bold;">Ширина поля ввода:</label>
                <div style="display:flex; align-items:center; gap:10px;">
                    <input type="range" id="slider-inputWidth" min="50" max="200" value="${currentSettings.inputWidth}" style="flex:1;">
                    <input type="number" id="input-inputWidth" value="${currentSettings.inputWidth}" min="50" max="200" style="width:70px;">
                    <span>%</span>
                </div>
                <small style="color:#a0aec0; font-size:12px;">Оригинал: ${ORIGINAL_VALUES.INPUT_WIDTH}px (100%)</small>
            </div>

            <div style="margin-bottom:15px;">
                <label style="display:block; margin-bottom:5px; font-weight:bold;">Высота поля ввода:</label>
                <div style="display:flex; align-items:center; gap:10px;">
                    <input type="range" id="slider-inputHeight" min="100" max="500" value="${currentSettings.inputHeight}" style="flex:1;">
                    <input type="number" id="input-inputHeight" value="${currentSettings.inputHeight}" min="100" max="500" style="width:70px;">
                    <span>%</span>
                </div>
                <small style="color:#a0aec0; font-size:12px;">Оригинал: ${ORIGINAL_VALUES.TEXTAREA_MIN_HEIGHT}px - ${ORIGINAL_VALUES.TEXTAREA_MAX_HEIGHT}px (100%)</small>
            </div>

            <div style="margin-bottom:15px;">
                <label style="display:block; margin-bottom:5px; font-weight:bold;">Выравнивание текста сообщений:</label>
                <select id="message-align" style="width:100%;padding:8px;border-radius:4px;background:#4a5568;color:white;border:1px solid #718096;">
                    <option value="left" ${currentSettings.messageAlign === 'left' ? 'selected' : ''}>По левому краю</option>
                    <option value="center" ${currentSettings.messageAlign === 'center' ? 'selected' : ''}>По центру</option>
                    <option value="right" ${currentSettings.messageAlign === 'right' ? 'selected' : ''}>По правому краю</option>
                </select>
            </div>

            <h3 style="margin:20px 0 15px 0;">Настройки шрифта</h3>

            <div style="margin-bottom:15px;">
                <label style="display:block; margin-bottom:5px; font-weight:bold;">Размер шрифта:</label>
                <div style="display:flex; align-items:center; gap:10px;">
                    <input type="range" id="slider-fontSize" min="50" max="200" value="${currentSettings.fontSize}" style="flex:1;">
                    <input type="number" id="input-fontSize" value="${currentSettings.fontSize}" min="50" max="200" style="width:70px;">
                    <span>%</span>
                </div>
                <small style="color:#a0aec0; font-size:12px;">Оригинал: 100% (максимум: 200%)</small>
            </div>

            ${createFontFamilyControl()}

            <div style="display:flex; gap:10px; margin-top:20px;">
                <button id="btn-apply" style="flex:1;background:#3182ce;color:white;padding:10px;border:none;border-radius:4px;cursor:pointer;font-weight:bold;">Применить</button>
                <button id="btn-reset" style="flex:1;background:#fc8181;color:white;padding:10px;border:none;border-radius:4px;cursor:pointer;font-weight:bold;">Сбросить</button>
            </div>
        `;

        const switchStyles = `
            .switch {
                position: relative;
                display: inline-block;
                width: 50px;
                height: 24px;
            }
            .switch input {
                opacity: 0;
                width: 0;
                height: 0;
            }
            .slider {
                position: absolute;
                cursor: pointer;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background-color: #e53e3e;
                transition: .4s;
                border-radius: 24px;
            }
            .slider:before {
                position: absolute;
                content: "";
                height: 16px;
                width: 16px;
                left: 4px;
                bottom: 4px;
                background-color: white;
                transition: .4s;
                border-radius: 50%;
            }
            input:checked + .slider {
                background-color: #48bb78;
            }
            input:checked + .slider:before {
                transform: translateX(26px);
            }
        `;

        const style = document.createElement('style');
        style.textContent = switchStyles;
        panel.appendChild(style);

        document.body.appendChild(panel);
        setupPanelHandlers(panel);
    }

    function setupPanelHandlers(panel) {
        // Обработчики для всех слайдеров
        function setupSliderHandlers(key, unit = '%') {
            const slider = panel.querySelector(`#slider-${key}`);
            const input = panel.querySelector(`#input-${key}`);

            const updateValue = (val) => {
                const numVal = parseInt(val);
                if (!isNaN(numVal)) {
                    if (key === 'inputHeight' && numVal < 100) {
                        numVal = 100;
                    }
                    currentSettings[key] = numVal;
                    slider.value = numVal;
                    input.value = numVal;

                    // 1) При движении ползунков временно применяем стили
                    if (isScriptEnabled) {
                        applyStyles();
                        hasUnsavedChanges = true;
                    }
                }
            };

            slider.addEventListener('input', (e) => updateValue(e.target.value));
            input.addEventListener('input', (e) => updateValue(e.target.value));
        }

        ['chatWidth', 'inputWidth', 'inputHeight', 'fontSize'].forEach(key => setupSliderHandlers(key));

        // Выравнивание текста
        const messageAlign = panel.querySelector('#message-align');
        messageAlign.addEventListener('change', (e) => {
            currentSettings.messageAlign = e.target.value;
            if (isScriptEnabled) {
                applyStyles();
                hasUnsavedChanges = true;
            }
        });

        // Шрифт
        const fontFamily = panel.querySelector('#font-family');
        fontFamily.addEventListener('change', (e) => {
            currentSettings.fontFamily = e.target.value;
            if (isScriptEnabled) {
                applyStyles();
                hasUnsavedChanges = true;
            }
        });

        // Переключатель скрипта
        const scriptToggle = panel.querySelector('#script-toggle');
        scriptToggle.addEventListener('change', (e) => {
            isScriptEnabled = e.target.checked;
            updateButton();
            if (isScriptEnabled && hasUnsavedChanges) {
                applyStyles();
            } else if (!isScriptEnabled) {
                if (styleElement) {
                    styleElement.textContent = '';
                }
            }
            saveProfiles();
            showNotification(isScriptEnabled ? 'Скрипт включен' : 'Скрипт выключен', isScriptEnabled ? 'success' : 'info');
        });

        // Управление профилями
        const profileSelect = panel.querySelector('#profile-select');
        profileSelect.addEventListener('change', (e) => {
            const profileName = e.target.value;

            // 3) При сохранении настроек в профиль или выборе сохраненного профиля, черновик удаляется
            hasUnsavedChanges = false;

            currentProfileName = profileName;

            if (profileName === 'Профиль не выбран') {
                // Отключаем стили
                if (styleElement) {
                    styleElement.textContent = '';
                }
                currentSettings = {...DEFAULT_SETTINGS};
                // Обновляем кнопку удаления профиля
                const btnDeleteProfile = panel.querySelector('#btn-delete-profile');
                if (btnDeleteProfile) {
                    btnDeleteProfile.disabled = true;
                }
            } else {
                // Загружаем настройки профиля
                if (profiles[profileName]) {
                    currentSettings = {...profiles[profileName]};

                    if (currentSettings.inputHeight < 100) {
                        currentSettings.inputHeight = 100;
                    }

                    // Обновляем кнопку удаления профиля
                    const btnDeleteProfile = panel.querySelector('#btn-delete-profile');
                    if (btnDeleteProfile) {
                        btnDeleteProfile.disabled = false;
                    }

                    if (isScriptEnabled) {
                        applyStyles();
                    }
                }
            }
            updatePanelUI();
            saveProfiles();
        });

        const btnDeleteProfile = panel.querySelector('#btn-delete-profile');
        btnDeleteProfile.addEventListener('click', () => {
            if (currentProfileName === 'Профиль не выбран') return;

            if (confirm(`Удалить профиль "${currentProfileName}"?`)) {
                delete profiles[currentProfileName];
                // Переключаемся на "Профиль не выбран"
                currentProfileName = 'Профиль не выбран';
                currentSettings = {...DEFAULT_SETTINGS};
                updatePanelUI();
                // Отключаем стили
                if (styleElement) {
                    styleElement.textContent = '';
                }
                saveProfiles();
                showNotification('Профиль удален', 'info');
            }
        });

        const btnSaveProfile = panel.querySelector('#btn-save-profile');
        const newProfileName = panel.querySelector('#new-profile-name');
        btnSaveProfile.addEventListener('click', () => {
            const name = newProfileName.value.trim();
            if (!name) {
                showNotification('Введите название профиля', 'error');
                return;
            }
            profiles[name] = {...currentSettings};
            currentProfileName = name;
            newProfileName.value = '';
            // 3) Черновик удаляется при сохранении профиля
            hasUnsavedChanges = false;
            updatePanelUI();
            saveProfiles();
            showNotification(`Профиль "${name}" сохранен`, 'success');
        });

        // Кнопка "Применить"
        panel.querySelector('#btn-apply').addEventListener('click', () => {
            const profileNameInput = panel.querySelector('#new-profile-name');
            const profileName = profileNameInput.value.trim();

            if (profileName) {
                // Сохраняем как новый профиль
                profiles[profileName] = {...currentSettings};
                currentProfileName = profileName;
                profileNameInput.value = '';
                // 2) При нажатии кнопки применить стиль сохраняется как Черновик
                hasUnsavedChanges = true;
                updatePanelUI();
                saveProfiles();
                showNotification(`Профиль "${profileName}" сохранен и применен!`, 'success');
            } else {
                // 2) При нажатии кнопки применить стиль сохраняется как Черновик
                hasUnsavedChanges = true;
                if (isScriptEnabled) {
                    applyStyles();
                }
                showNotification('Настройки применены (черновик)!', 'success');
            }
        });

        // Кнопка "Сбросить"
        panel.querySelector('#btn-reset').addEventListener('click', () => {
            if (confirm('Сбросить настройки к оригинальным?')) {
                // 4) При нажатии кнопки Сброс, черновик просто удаляется
                hasUnsavedChanges = false;
                currentSettings = {...DEFAULT_SETTINGS};
                currentProfileName = 'Профиль не выбран';
                updatePanelUI();
                // Отключаем стили
                if (styleElement) {
                    styleElement.textContent = '';
                }
                saveProfiles();
                showNotification('Настройки сброшены!', 'info');
            }
        });

        panel.querySelector('#btn-close').addEventListener('click', togglePanel);
    }

    function updatePanelUI() {
        if (!isPanelOpen) return;

        const panel = document.getElementById('spicychat-editor-panel');
        if (!panel) return;

        ['chatWidth', 'inputWidth', 'inputHeight', 'fontSize'].forEach(key => {
            const val = currentSettings[key];
            const slider = panel.querySelector(`#slider-${key}`);
            const input = panel.querySelector(`#input-${key}`);
            if (slider) slider.value = val;
            if (input) input.value = val;
        });

        const messageAlign = panel.querySelector('#message-align');
        if (messageAlign) messageAlign.value = currentSettings.messageAlign;

        const fontFamily = panel.querySelector('#font-family');
        if (fontFamily) fontFamily.value = currentSettings.fontFamily;

        const scriptToggle = panel.querySelector('#script-toggle');
        if (scriptToggle) scriptToggle.checked = isScriptEnabled;

        // Обновляем список профилей и кнопку удаления
        const profileSelect = panel.querySelector('#profile-select');
        if (profileSelect) {
            let options = '<option value="Профиль не выбран"' + (currentProfileName === 'Профиль не выбран' ? ' selected' : '') + '>Профиль не выбран</option>';
            options += Object.keys(profiles).map(name =>
                `<option value="${name}" ${currentProfileName === name ? 'selected' : ''}>${name}</option>`
            ).join('');
            profileSelect.innerHTML = options;
        }

        const btnDeleteProfile = panel.querySelector('#btn-delete-profile');
        if (btnDeleteProfile) {
            btnDeleteProfile.disabled = currentProfileName === 'Профиль не выбран';
        }
    }

    function showNotification(message, type = 'info') {
        const notification = document.createElement('div');
        notification.textContent = message;
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: ${type === 'success' ? '#48bb78' : type === 'error' ? '#fc8181' : '#3182ce'};
            color: white;
            padding: 10px 20px;
            border-radius: 4px;
            z-index: 10001;
            font-weight: bold;
            box-shadow: 0 2px 10px rgba(0,0,0,0.3);
        `;

        document.body.appendChild(notification);

        setTimeout(() => {
            if (notification.parentNode) {
                notification.parentNode.removeChild(notification);
            }
        }, 2000);
    }

    function togglePanel() {
        const panel = document.getElementById('spicychat-editor-panel');

        if (!isPanelOpen) {
            if (!panel) createPanel();
            panel.style.display = 'block';
            updatePanelUI();
        } else {
            if (panel) panel.style.display = 'none';
        }
        isPanelOpen = !isPanelOpen;
    }

    // Закрытие панели при клике вне ее
    document.addEventListener('click', function(event) {
        const panel = document.getElementById('spicychat-editor-panel');
        const button = document.getElementById('spicychat-editor-button');

        if (isPanelOpen && panel && !panel.contains(event.target) &&
            button && !button.contains(event.target)) {
            togglePanel();
        }
    });

    // Запуск
    function init() {
        loadProfiles();
        createButton();
        // Применяем стили только если есть активный профиль
        if (currentProfileName !== 'Профиль не выбран' && isScriptEnabled) {
            applyStyles();
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();