Yandex Solver v4

Yandex Education Solver - Auto Mode

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name         Yandex Solver v4
// @namespace    http://tampermonkey.net/
// @version      40.4
// @description  Yandex Education Solver - Auto Mode
// @author       Nirupha
// @match        https://education.yandex.ru/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    const SETTINGS_KEY = 'ye_settings';
    const SUBJECT_ID = 'ac7328ca-dd3d-4bea-8566-9c3177273a57';
    const BASE_URL = 'https://education.yandex.ru';
    const AUTO_ANSWERS_KEY = 'ye_auto_answers';
    const AUTO_MODE_KEY = 'ye_auto_mode';
    const HIDDEN_KEY = 'ye_hidden';
    const COLLECTION_INFO_KEY = 'ye_collection_info';

    function isValidPage() {
        return /\/(collections|variants)\//.test(window.location.href);
    }

    function getCollectionId() {
        const match = window.location.href.match(/\/(collections|variants)\/([^\/\?]+)/);
        return match ? match[2] : null;
    }

    function getAnswersLogKey(collectionId) {
        return `ye_answers_log_${collectionId || 'default'}`;
    }

    function getSettings() {
        try {
            const saved = localStorage.getItem(SETTINGS_KEY);
            if (saved) return JSON.parse(saved);
        } catch(e) {}
        return { showWrong: true, autoFill: false };
    }

    function saveSettings(settings) {
        localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
    }

    function getCollectionTaskNumber() {
        const match = window.location.href.match(/\/(collections|variants)\/[^\/]+\/task\/(\d+)/);
        return match ? match[2] : null;
    }

    function getCollectionBaseUrl() {
        const match = window.location.href.match(/(.*\/(collections|variants)\/[^\/]+)/);
        return match ? match[1] : null;
    }

    function getCollectionInfo() {
        const collectionId = getCollectionId();
        if (!collectionId) return null;
        try {
            const saved = localStorage.getItem(`${COLLECTION_INFO_KEY}_${collectionId}`);
            if (saved) return JSON.parse(saved);
        } catch(e) {}
        return null;
    }

    function saveCollectionInfo(title) {
        const collectionId = getCollectionId();
        if (!collectionId) return;
        const url = getCollectionBaseUrl();
        localStorage.setItem(`${COLLECTION_INFO_KEY}_${collectionId}`, JSON.stringify({ title, url }));
    }

    function isHidden() {
        return localStorage.getItem(HIDDEN_KEY) === 'true';
    }

    function setHidden(hidden) {
        if (hidden) {
            localStorage.setItem(HIDDEN_KEY, 'true');
        } else {
            localStorage.removeItem(HIDDEN_KEY);
        }
        updateVisibility();
    }

    function toggleHidden() {
        setHidden(!isHidden());
    }

    function updateVisibility() {
        const container = document.getElementById('ye-container');
        if (container) {
            container.style.display = isHidden() ? 'none' : 'block';
        }
    }

    function setupHiddenButton() {
        const badge = document.querySelector('[class*="EgeLevelBadge-module__EgeLevelBadge"]');
        if (!badge || badge.dataset.yeSetup) return;

        badge.dataset.yeSetup = 'true';
        badge.style.cursor = 'pointer';

        badge.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            toggleHidden();
        });
    }

    async function getCsrfToken() {
        const response = await fetch(BASE_URL + '/api/v5/get-csrf-token', {
            method: 'GET',
            credentials: 'include'
        });
        const data = await response.json();
        return data.sk;
    }

    async function getTaskAnswer(taskId) {
        const csrfToken = await getCsrfToken();

        const response = await fetch(BASE_URL + '/api/v5/gpttr', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'x-csrf-token': csrfToken
            },
            body: JSON.stringify([{
                type: 'get_task_by_id',
                task_id: taskId,
                params: {
                    subject_id: SUBJECT_ID
                }
            }]),
            credentials: 'include'
        });

        const data = await response.json();

        const answerLayout = data.markup?.answer_control_layout;
        if (answerLayout && answerLayout.length > 0) {
            const content = answerLayout[0].content;
            if (content?.correct_answers) {
                return content.correct_answers;
            }
        }

        return null;
    }

    async function getAllCollectionAnswers(variantId) {
        const csrfToken = await getCsrfToken();

        const response = await fetch(BASE_URL + '/api/v5/gpttr', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'x-csrf-token': csrfToken
            },
            body: JSON.stringify([{
                type: 'public_get_variant_request_item',
                variant_id: variantId,
                params: {
                    subject_id: SUBJECT_ID
                }
            }]),
            credentials: 'include'
        });

        const data = await response.json();

        const answers = {};
        let totalTasks = 0;
        let title = data.title || 'Подборка';

        saveCollectionInfo(title);

        if (data.tasks && Array.isArray(data.tasks)) {
            totalTasks = data.tasks.length;
            data.tasks.forEach((task, index) => {
                const num = index + 1;
                const answerLayout = task.markup?.answer_control_layout;

                if (answerLayout && answerLayout.length > 0) {
                    const content = answerLayout[0].content;

                    if (content?.correct_answers && typeof content.correct_answers === 'string') {
                        answers[num] = {
                            answer: content.correct_answers,
                            type: 'text',
                            taskId: task.id,
                            egeNumber: task.number
                        };
                    }
                    else if (content?.correct_answers && Array.isArray(content.correct_answers)) {
                        answers[num] = {
                            answer: content.correct_answers,
                            type: 'table',
                            taskId: task.id,
                            egeNumber: task.number
                        };
                    }
                }
            });
        }

        return { answers, totalTasks, title };
    }

    async function getTaskIdFromPage() {
        const copyBtn = document.querySelector('[data-testid="GpttrTaskTitleOverlappingButton"]');
        if (copyBtn) {
            copyBtn.click();
            await new Promise(r => setTimeout(r, 100));

            try {
                const url = await navigator.clipboard.readText();
                const match = url.match(/\/task\/([a-f0-9-]+)/);
                if (match) return match[1];
            } catch(e) {}
        }

        const links = document.querySelectorAll('a[href*="/task/"]');
        for (const link of links) {
            const match = link.href.match(/\/task\/([a-f0-9-]{36})/);
            if (match) return match[1];
        }

        return null;
    }

    function getAnswersLog() {
        const collectionId = getCollectionId();
        if (!collectionId) return {};
        try {
            const saved = localStorage.getItem(getAnswersLogKey(collectionId));
            if (saved) return JSON.parse(saved);
        } catch(e) {}
        return {};
    }

    function saveAnswerToLog(taskNum, userAnswer, isCorrect, correctAnswer, egeNumber) {
        const collectionId = getCollectionId();
        if (!collectionId) return;

        const key = getAnswersLogKey(collectionId);
        let log = getAnswersLog();
        log[taskNum] = {
            answer: userAnswer,
            isCorrect,
            correctAnswer,
            egeNumber,
            timestamp: Date.now()
        };
        localStorage.setItem(key, JSON.stringify(log));
    }

    function saveAllAnswersToLog(answers) {
        const collectionId = getCollectionId();
        if (!collectionId) return;

        const key = getAnswersLogKey(collectionId);
        let log = {};

        for (const [num, data] of Object.entries(answers)) {
            log[num] = {
                answer: data.answer,
                isCorrect: true,
                correctAnswer: data.answer,
                timestamp: Date.now(),
                type: data.type,
                egeNumber: data.egeNumber
            };
        }

        localStorage.setItem(key, JSON.stringify(log));
    }

    function deleteAnswerFromLog(taskNum) {
        const collectionId = getCollectionId();
        if (!collectionId) return;

        const key = getAnswersLogKey(collectionId);
        let log = getAnswersLog();
        delete log[taskNum];
        localStorage.setItem(key, JSON.stringify(log));
        forceRenderAnswersPanel();
    }

    function clearCurrentAnswersLog() {
        const collectionId = getCollectionId();
        if (!collectionId) return;
        localStorage.removeItem(getAnswersLogKey(collectionId));
    }

    function formatAnswerForCopy(data) {
        if (typeof data.answer === 'string') {
            return data.answer;
        }
        if (Array.isArray(data.answer)) {
            return data.answer.map(row => {
                if (Array.isArray(row)) return row.join(', ');
                return row;
            }).join(' | ');
        }
        return String(data.answer);
    }

    async function copyAnswersToClipboard() {
        const log = getAnswersLog();
        const entries = Object.entries(log);

        if (entries.length === 0) {
            alert('Нет ответов для копирования. Сначала загрузи ответы.');
            return false;
        }

        const info = getCollectionInfo();
        const title = info?.title || 'Подборка';
        const url = info?.url || getCollectionBaseUrl() || window.location.href;

        entries.sort((a, b) => parseInt(a[0]) - parseInt(b[0]));

        let text = `**${title}**\n`;
        text += `Ссылка:\n`;
        text += `**>${url}**\n\n`;

        entries.forEach(([num, data]) => {
            const answer = formatAnswerForCopy(data);
            text += `${num}: \`${answer}\`\n`;
        });

        try {
            await navigator.clipboard.writeText(text);
            return true;
        } catch (e) {
            console.error('Copy failed:', e);
            const textarea = document.createElement('textarea');
            textarea.value = text;
            textarea.style.cssText = 'position:fixed;top:-9999px;';
            document.body.appendChild(textarea);
            textarea.select();
            document.execCommand('copy');
            document.body.removeChild(textarea);
            return true;
        }
    }

    function getAutoAnswers() {
        try {
            const saved = localStorage.getItem(AUTO_ANSWERS_KEY);
            if (saved) return JSON.parse(saved);
        } catch(e) {}
        return null;
    }

    function saveAutoAnswers(answers, totalTasks) {
        localStorage.setItem(AUTO_ANSWERS_KEY, JSON.stringify({ answers, totalTasks }));
    }

    function clearAutoAnswers() {
        localStorage.removeItem(AUTO_ANSWERS_KEY);
    }

    function isAutoMode() {
        return localStorage.getItem(AUTO_MODE_KEY) === 'true';
    }

    function setAutoMode(enabled) {
        if (enabled) {
            localStorage.setItem(AUTO_MODE_KEY, 'true');
        } else {
            localStorage.removeItem(AUTO_MODE_KEY);
        }
    }

    function autoFillPageInput(answer) {
        const pageInput = document.querySelector('.TaskTextinputAnswer-module__GpttrTaskTextinputAnswer_Textinput--NmcgF input.Textinput-Control');
        if (!pageInput || !answer) return false;

        const clearBtn = document.querySelector('.TaskTextinputAnswer-module__GpttrTaskTextinputAnswer_Textinput--NmcgF .Textinput-Clear');
        if (clearBtn && pageInput.value) clearBtn.click();

        const lastValue = pageInput.value;
        pageInput.value = answer;
        pageInput.focus();

        const event = new Event("input", { bubbles: true });
        const tracker = pageInput._valueTracker;
        if (tracker) tracker.setValue(lastValue);
        pageInput.dispatchEvent(event);

        pageInput.style.backgroundColor = '#e8f5e9';
        pageInput.style.borderColor = '#4caf50';
        pageInput.style.boxShadow = '0 0 0 2px rgba(76,175,80,0.3)';

        setTimeout(() => {
            pageInput.style.backgroundColor = '';
            pageInput.style.borderColor = '';
            pageInput.style.boxShadow = '';
        }, 400);

        return true;
    }

    function clickSaveButton() {
        const saveBtn = document.querySelector('button.Button2_type_submit');
        if (saveBtn && saveBtn.textContent.includes('Сохранить')) {
            saveBtn.click();
            return true;
        }
        return false;
    }

    function goToTask(taskNum) {
        const baseUrl = getCollectionBaseUrl();
        if (baseUrl) {
            window.location.href = `${baseUrl}/task/${taskNum}`;
            return true;
        }
        return false;
    }

    async function waitForElement(selector, timeout = 5000) {
        const start = Date.now();
        while (Date.now() - start < timeout) {
            const el = document.querySelector(selector);
            if (el) return el;
            await new Promise(r => setTimeout(r, 100));
        }
        return null;
    }

    async function waitForSaveComplete(timeout = 5000) {
        const start = Date.now();
        while (Date.now() - start < timeout) {
            const saveBtn = document.querySelector('button.Button2_type_submit');
            if (saveBtn && !saveBtn.disabled) {
                await new Promise(r => setTimeout(r, 500));
                return true;
            }
            await new Promise(r => setTimeout(r, 100));
        }
        return true;
    }

    async function runAutoMode() {
        const data = getAutoAnswers();
        if (!data) {
            setAutoMode(false);
            updateAutoButton();
            return;
        }

        const { answers, totalTasks } = data;
        const currentNum = getCollectionTaskNumber();

        if (!currentNum) {
            await new Promise(r => setTimeout(r, 500));
            runAutoMode();
            return;
        }

        const numInt = parseInt(currentNum);
        const answer = answers[numInt];

        updateAutoButton(numInt, totalTasks);

        if (!answer) {
            console.log(`Нет ответа для задания ${numInt}`);
            if (numInt < totalTasks) {
                goToTask(numInt + 1);
            } else {
                setAutoMode(false);
                clearAutoAnswers();
                updateAutoButton();
                alert('✅ Автоматическое прохождение завершено!');
            }
            return;
        }

        await waitForElement('.TaskTextinputAnswer-module__GpttrTaskTextinputAnswer_Textinput--NmcgF input.Textinput-Control');
        await new Promise(r => setTimeout(r, 300));

        if (!isAutoMode()) {
            updateAutoButton();
            return;
        }

        const answerText = typeof answer.answer === 'string'
            ? answer.answer
            : (Array.isArray(answer.answer) ? answer.answer.flat().join(', ') : String(answer.answer));

        const filled = autoFillPageInput(answerText);
        if (!filled) {
            console.log('Не удалось вставить ответ');
            if (numInt < totalTasks) {
                goToTask(numInt + 1);
            }
            return;
        }

        await new Promise(r => setTimeout(r, 300));

        if (!isAutoMode()) {
            updateAutoButton();
            return;
        }

        const clicked = clickSaveButton();
        if (!clicked) {
            console.log('Не удалось нажать кнопку сохранения');
        }

        await waitForSaveComplete();

        saveAnswerToLog(currentNum, answerText, true, answerText, answer.egeNumber);

        if (!isAutoMode()) {
            updateAutoButton();
            forceRenderAnswersPanel();
            return;
        }

        if (numInt < totalTasks) {
            await new Promise(r => setTimeout(r, 200));
            goToTask(numInt + 1);
        } else {
            setAutoMode(false);
            clearAutoAnswers();
            updateAutoButton();
            forceRenderAnswersPanel();
            alert('✅ Автоматическое прохождение завершено!');
        }
    }

    function updateAutoButton(current, total) {
        const btn = document.getElementById('ye-auto-btn');
        if (!btn) return;

        if (isAutoMode() && current && total) {
            btn.textContent = `⏸ ${current}/${total}`;
            btn.style.background = '#ff9800';
        } else if (isAutoMode()) {
            btn.textContent = '⏸ Стоп';
            btn.style.background = '#ff9800';
        } else {
            btn.textContent = '▶ Авто';
            btn.style.background = '#9c27b0';
        }
    }

    function createToggle(id, label, initialChecked, onChange) {
        const wrapper = document.createElement('div');
        wrapper.style.cssText = `display:flex;align-items:center;gap:6px;`;

        const toggle = document.createElement('div');
        toggle.id = id;
        toggle.dataset.checked = initialChecked ? 'true' : 'false';
        toggle.style.cssText = `width:32px;height:18px;background:${initialChecked ? '#4caf50' : '#ccc'};border-radius:9px;position:relative;cursor:pointer;transition:background 0.2s;flex-shrink:0;`;

        const knob = document.createElement('div');
        knob.style.cssText = `width:14px;height:14px;background:#fff;border-radius:50%;position:absolute;top:2px;left:${initialChecked ? '16px' : '2px'};transition:left 0.2s;box-shadow:0 1px 3px rgba(0,0,0,0.3);`;
        toggle.appendChild(knob);

        const labelEl = document.createElement('span');
        labelEl.textContent = label;
        labelEl.style.cssText = `font-size:11px;color:#666;white-space:nowrap;cursor:pointer;`;

        const handleClick = () => {
            const isCurrentlyChecked = toggle.dataset.checked === 'true';
            const newState = !isCurrentlyChecked;
            toggle.dataset.checked = newState ? 'true' : 'false';
            toggle.style.background = newState ? '#4caf50' : '#ccc';
            knob.style.left = newState ? '16px' : '2px';
            onChange(newState);
        };

        toggle.onclick = handleClick;
        labelEl.onclick = handleClick;

        wrapper.appendChild(toggle);
        wrapper.appendChild(labelEl);
        return wrapper;
    }

    let lastRenderedHash = '';

    function forceRenderAnswersPanel() {
        lastRenderedHash = '';
        renderAnswersPanel();
    }

    function formatAnswer(data) {
        if (typeof data.answer === 'string') {
            return data.answer;
        }
        if (Array.isArray(data.answer)) {
            return data.answer.map(row => {
                if (Array.isArray(row)) return row.join(', ');
                return row;
            }).join(' | ');
        }
        return String(data.answer);
    }

    function renderAnswersPanel() {
        if (!isValidPage()) return;

        const container = document.getElementById('ye-container');
        if (!container) return;

        const log = getAnswersLog();
        const entries = Object.entries(log);
        const settings = getSettings();

        let panel = document.getElementById('ye-answers-panel');
        const currentHash = JSON.stringify(log) + JSON.stringify(settings);

        if (panel && currentHash === lastRenderedHash) return;
        lastRenderedHash = currentHash;

        if (entries.length === 0) {
            if (panel) panel.remove();
            return;
        }

        if (panel) panel.remove();

        panel = document.createElement('div');
        panel.id = 'ye-answers-panel';
        panel.style.cssText = `display:flex;flex-direction:column;gap:4px;margin-top:10px;padding:10px 12px;background:rgba(0,0,0,0.03);border-radius:8px;font-size:13px;max-height:400px;overflow-y:auto;`;
        container.appendChild(panel);

        entries.sort((a, b) => parseInt(a[0]) - parseInt(b[0]));

        const currentNum = getCollectionTaskNumber();

        entries.forEach(([num, data]) => {
            const isCorrect = data.isCorrect === true;
            const displayAnswer = formatAnswer(data);
            const isCurrent = num === currentNum;

            const item = document.createElement('div');
            item.style.cssText = `display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:${isCurrent ? 'rgba(33,150,243,0.1)' : 'rgba(0,0,0,0.03)'};border-radius:4px;position:relative;cursor:pointer;align-self:flex-start;${isCurrent ? 'border:1px solid rgba(33,150,243,0.3);' : ''}`;

            item.onclick = () => {
                autoFillPageInput(typeof data.answer === 'string' ? data.answer : displayAnswer);
            };

            if (isCorrect) {
                const symSpan = document.createElement('span');
                symSpan.style.cssText = `color:#4caf50;font-weight:600;`;
                symSpan.textContent = '✓';
                item.appendChild(symSpan);
            }

            const numSpan = document.createElement('span');
            numSpan.style.cssText = `opacity:0.5;font-weight:600;`;
            const egeNum = data.egeNumber ? ` (№${data.egeNumber})` : '';
            numSpan.textContent = `${num}${egeNum}:`;

            const answerSpan = document.createElement('span');

            if (isCorrect) {
                answerSpan.innerHTML = `<b>${displayAnswer}</b>`;
            } else if (data.correctAnswer) {
                if (settings.showWrong) {
                    answerSpan.innerHTML = `<b style="color:#e53935">${data.answer}</b> → <b style="color:#4caf50">${data.correctAnswer}</b>`;
                } else {
                    answerSpan.innerHTML = `<b style="color:#4caf50">${data.correctAnswer}</b>`;
                }
            } else {
                answerSpan.innerHTML = `<b style="color:#e53935">${data.answer}</b>`;
            }

            const deleteBtn = document.createElement('span');
            deleteBtn.textContent = '✕';
            deleteBtn.style.cssText = `position:absolute;top:-6px;right:-6px;width:16px;height:16px;background:#e53935;color:#fff;border-radius:50%;font-size:10px;display:none;align-items:center;justify-content:center;cursor:pointer;line-height:16px;text-align:center;`;
            deleteBtn.onclick = (e) => {
                e.stopPropagation();
                deleteAnswerFromLog(num);
            };

            item.onmouseenter = () => { deleteBtn.style.display = 'flex'; };
            item.onmouseleave = () => { deleteBtn.style.display = 'none'; };

            item.appendChild(numSpan);
            item.appendChild(answerSpan);
            item.appendChild(deleteBtn);
            panel.appendChild(item);
        });

        const clearAll = document.createElement('button');
        clearAll.textContent = '🗑';
        clearAll.title = 'Очистить';
        clearAll.style.cssText = `padding:4px 8px;background:rgba(0,0,0,0.08);border:none;border-radius:4px;font-size:12px;cursor:pointer;align-self:flex-end;margin-top:4px;`;
        clearAll.onclick = () => {
            if (confirm('Очистить историю?')) {
                clearCurrentAnswersLog();
                panel.remove();
                lastRenderedHash = '';
            }
        };
        panel.appendChild(clearAll);
    }

    function findPanelParent() {
        const aiMessage = document.querySelector('[class*="ChatAssistantMessage-module__assistant_message_border"]');
        if (aiMessage && aiMessage.parentElement) {
            return { parent: aiMessage.parentElement, insertAfter: aiMessage };
        }

        const taskBubble = document.querySelector('[class*="GpttrTaskDetails-module__GpttrTaskBubble--"]');
        if (taskBubble) {
            const footer = taskBubble.querySelector('footer');
            if (footer) {
                return { parent: footer, insertAfter: footer.querySelector('form') };
            }
            const canvas = taskBubble.querySelector('canvas');
            if (canvas) {
                return { parent: taskBubble, insertBefore: canvas };
            }
            return { parent: taskBubble, insertAfter: null };
        }

        const answerForm = document.querySelector('[data-testid="TaskTextinputAnswerForm"]');
        if (answerForm && answerForm.parentElement) {
            return { parent: answerForm.parentElement, insertAfter: answerForm };
        }

        return null;
    }

    let lastUrl = window.location.href;

    function checkUrlChange() {
        const currentUrl = window.location.href;
        if (currentUrl !== lastUrl) {
            lastUrl = currentUrl;
            lastRenderedHash = '';
        }
    }

    async function checkAnswer(userAnswer) {
        const taskNum = getCollectionTaskNumber();
        if (!taskNum) throw new Error('Не удалось определить номер задания');

        const taskId = await getTaskIdFromPage();
        if (!taskId) throw new Error('Не удалось получить ID задания');

        const correctAnswer = await getTaskAnswer(taskId);
        if (!correctAnswer) throw new Error('Не удалось получить правильный ответ');

        const isCorrect = userAnswer.trim() === correctAnswer.trim();

        return { isCorrect, correctAnswer, taskNum };
    }

    function createControlPanel() {
        checkUrlChange();
        setupHiddenButton();

        if (isHidden()) {
            const existing = document.getElementById('ye-container');
            if (existing) existing.style.display = 'none';
            return;
        }

        const existingContainer = document.getElementById('ye-container');

        const placement = findPanelParent();
        if (!placement) return;

        if (existingContainer && placement.parent.contains(existingContainer)) {
            existingContainer.style.display = 'block';
            return;
        }
        if (existingContainer) existingContainer.remove();

        const taskNum = getCollectionTaskNumber() || '?';
        const settings = getSettings();
        const collectionId = getCollectionId();

        const container = document.createElement('div');
        container.id = 'ye-container';
        container.style.cssText = `margin-top:12px;margin-bottom:12px;display:block;`;

        const panel = document.createElement('div');
        panel.id = 'ye-panel';
        panel.style.cssText = `display:flex;align-items:center;gap:8px;padding:10px 14px;background:rgba(0,0,0,0.05);border-radius:8px;flex-wrap:wrap;`;

        const numLabel = document.createElement('span');
        numLabel.id = 'ye-task-num';
        numLabel.textContent = `№${taskNum}`;
        numLabel.style.cssText = `font-size:15px;font-weight:600;color:#4caf50;min-width:30px;`;

        const sep1 = document.createElement('div');
        sep1.style.cssText = `width:1px;height:20px;background:rgba(0,0,0,0.1);`;

        const showAllBtn = document.createElement('button');
        showAllBtn.id = 'ye-show-all-btn';
        showAllBtn.textContent = '📋';
        showAllBtn.title = 'Показать все ответы';
        showAllBtn.style.cssText = `padding:6px 10px;background:#2196F3;color:#fff;border:none;border-radius:6px;font-size:14px;cursor:pointer;`;

        showAllBtn.onclick = async () => {
            if (!collectionId) {
                alert('Не удалось определить ID подборки');
                return;
            }

            showAllBtn.textContent = '⏳';
            showAllBtn.disabled = true;

            try {
                const { answers, totalTasks, title } = await getAllCollectionAnswers(collectionId);
                const count = Object.keys(answers).length;

                if (count === 0) {
                    alert('Не удалось получить ответы');
                    return;
                }

                saveAllAnswersToLog(answers);
                forceRenderAnswersPanel();

                showAllBtn.textContent = '✓';
                showAllBtn.style.background = '#4caf50';

                setTimeout(() => {
                    showAllBtn.textContent = '📋';
                    showAllBtn.style.background = '#2196F3';
                    showAllBtn.disabled = false;
                }, 1000);

            } catch (err) {
                console.error(err);
                alert('Ошибка: ' + err.message);
                showAllBtn.textContent = '📋';
                showAllBtn.disabled = false;
            }
        };

        const copyBtn = document.createElement('button');
        copyBtn.id = 'ye-copy-btn';
        copyBtn.textContent = '📄';
        copyBtn.title = 'Копировать ответы';
        copyBtn.style.cssText = `padding:6px 10px;background:#607D8B;color:#fff;border:none;border-radius:6px;font-size:14px;cursor:pointer;`;

        copyBtn.onclick = async () => {
            copyBtn.textContent = '⏳';
            copyBtn.disabled = true;

            const log = getAnswersLog();
            const hasAnswers = Object.keys(log).length > 0;

            if (!hasAnswers && collectionId) {
                try {
                    const { answers } = await getAllCollectionAnswers(collectionId);
                    saveAllAnswersToLog(answers);
                    forceRenderAnswersPanel();
                } catch (err) {
                    console.error(err);
                    alert('Ошибка загрузки: ' + err.message);
                    copyBtn.textContent = '📄';
                    copyBtn.disabled = false;
                    return;
                }
            }

            const success = await copyAnswersToClipboard();

            if (success) {
                copyBtn.textContent = '✓';
                copyBtn.style.background = '#4caf50';
            } else {
                copyBtn.textContent = '✗';
                copyBtn.style.background = '#e53935';
            }

            setTimeout(() => {
                copyBtn.textContent = '📄';
                copyBtn.style.background = '#607D8B';
                copyBtn.disabled = false;
            }, 1000);
        };

        const autoBtn = document.createElement('button');
        autoBtn.id = 'ye-auto-btn';
        autoBtn.textContent = isAutoMode() ? '⏸ Стоп' : '▶ Авто';
        autoBtn.title = 'Автоматическое прохождение';
        autoBtn.style.cssText = `padding:6px 10px;background:${isAutoMode() ? '#ff9800' : '#9c27b0'};color:#fff;border:none;border-radius:6px;font-size:12px;cursor:pointer;font-weight:bold;`;

        autoBtn.onclick = async () => {
            if (isAutoMode()) {
                setAutoMode(false);
                clearAutoAnswers();
                updateAutoButton();
                return;
            }

            if (!collectionId) {
                alert('Не удалось определить ID подборки');
                return;
            }

            autoBtn.textContent = '⏳';
            autoBtn.disabled = true;

            try {
                const { answers, totalTasks } = await getAllCollectionAnswers(collectionId);
                const count = Object.keys(answers).length;

                if (count === 0) {
                    alert('Не удалось получить ответы');
                    autoBtn.textContent = '▶ Авто';
                    autoBtn.disabled = false;
                    return;
                }

                saveAutoAnswers(answers, totalTasks);
                setAutoMode(true);

                saveAllAnswersToLog(answers);
                forceRenderAnswersPanel();

                autoBtn.disabled = false;
                updateAutoButton();

                runAutoMode();

            } catch (err) {
                console.error(err);
                alert('Ошибка: ' + err.message);
                autoBtn.textContent = '▶ Авто';
                autoBtn.disabled = false;
            }
        };

        const sep2 = document.createElement('div');
        sep2.style.cssText = `width:1px;height:20px;background:rgba(0,0,0,0.1);`;

        const togglesWrapper = document.createElement('div');
        togglesWrapper.style.cssText = `display:flex;gap:10px;align-items:center;`;

        const toggle1 = createToggle('ye-toggle-wrong', 'Неверные', settings.showWrong, (state) => {
            const s = getSettings();
            s.showWrong = state;
            saveSettings(s);
            forceRenderAnswersPanel();
        });

        togglesWrapper.appendChild(toggle1);

        const sep3 = document.createElement('div');
        sep3.style.cssText = `width:1px;height:20px;background:rgba(0,0,0,0.1);`;

        const input = document.createElement('input');
        input.id = 'ye-input';
        input.placeholder = "Ответ";
        input.style.cssText = `flex:1;padding:8px 10px;border-radius:6px;border:1px solid rgba(0,0,0,0.15);background:#fff;font-size:14px;outline:none;max-width:100px;min-width:60px;`;

        const sendBtn = document.createElement('button');
        sendBtn.id = 'ye-send-btn';
        sendBtn.innerHTML = "→";
        sendBtn.style.cssText = `padding:8px 12px;background:#4caf50;color:#fff;border:none;border-radius:6px;font-size:16px;font-weight:bold;cursor:pointer;`;

        input.onkeydown = (e) => { if (e.key === "Enter") sendBtn.click(); };

        sendBtn.onclick = async () => {
            const userAnswer = input.value.trim();
            if (!userAnswer) return alert("Введи ответ!");

            sendBtn.innerHTML = "···";
            sendBtn.style.background = "#999";
            sendBtn.disabled = true;

            try {
                const result = await checkAnswer(userAnswer);

                saveAnswerToLog(result.taskNum, userAnswer, result.isCorrect, result.correctAnswer);

                sendBtn.innerHTML = result.isCorrect ? "✓" : "✗";
                sendBtn.style.background = result.isCorrect ? "#4caf50" : "#e53935";

                if (!result.isCorrect && settings.autoFill) {
                    autoFillPageInput(result.correctAnswer);
                }

                forceRenderAnswersPanel();

                input.value = "";
                setTimeout(() => {
                    sendBtn.innerHTML = "→";
                    sendBtn.style.background = "#4caf50";
                    sendBtn.disabled = false;
                    input.focus();
                }, 300);

            } catch (err) {
                console.error(err);
                alert(err.message);
                sendBtn.innerHTML = "→";
                sendBtn.style.background = "#4caf50";
                sendBtn.disabled = false;
            }
        };

        panel.appendChild(numLabel);
        panel.appendChild(sep1);
        panel.appendChild(showAllBtn);
        panel.appendChild(copyBtn);
        panel.appendChild(autoBtn);
        panel.appendChild(sep2);
        panel.appendChild(togglesWrapper);
        panel.appendChild(sep3);
        panel.appendChild(input);
        panel.appendChild(sendBtn);
        container.appendChild(panel);

        if (placement.insertBefore) {
            placement.parent.insertBefore(container, placement.insertBefore);
        } else if (placement.insertAfter) {
            placement.insertAfter.after(container);
        } else {
            placement.parent.appendChild(container);
        }

        lastRenderedHash = '';
        renderAnswersPanel();

        if (!isAutoMode()) {
            input.focus();
        }
    }

    function updateTaskNum() {
        const numLabel = document.getElementById('ye-task-num');
        if (!numLabel) return;
        const num = getCollectionTaskNumber() || '?';
        if (numLabel.textContent !== `№${num}`) numLabel.textContent = `№${num}`;
    }

    function init() {
        if (!isValidPage()) return;

        if (isAutoMode()) {
            setTimeout(runAutoMode, 1000);
        }

        setInterval(() => {
            checkUrlChange();
            createControlPanel();
            updateTaskNum();
            renderAnswersPanel();
        }, 100);
    }

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

})();