Stepik Invisible Character Button

Adds invisible character button to Stepik comment editor

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         Stepik Invisible Character Button
// @namespace    https://stepik.org/
// @version      1.0
// @description  Adds invisible character button to Stepik comment editor
// @match        https://stepik.org/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const INVISIBLE_CHAR = '\u200B'; // Zero-width space

    function insertInvisibleChar() {
        const editor = document.querySelector('.rich-text-editor__content[contenteditable="true"]');
        if (!editor) return;

        // Insert invisible character at cursor position
        const selection = window.getSelection();
        const range = selection.getRangeAt(0);

        const textNode = document.createTextNode(INVISIBLE_CHAR);
        range.insertNode(textNode);

        // Move cursor after inserted character
        range.setStartAfter(textNode);
        range.setEndAfter(textNode);
        selection.removeAllRanges();
        selection.addRange(range);

        // Trigger input events to enable submit button
        editor.dispatchEvent(new Event('input', { bubbles: true }));
        editor.dispatchEvent(new Event('change', { bubbles: true }));
        editor.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true }));

        // Focus editor
        editor.focus();
    }

    function createButton() {
        const btn = document.createElement('a');
        btn.id = 'invisible-char-btn';
        btn.className = 'cke_button cke_button_off';
        btn.title = 'Insert Invisible Character';
        btn.tabIndex = '-1';
        btn.setAttribute('role', 'button');
        btn.style.cursor = 'pointer';

        const icon = document.createElement('span');
        icon.className = 'cke_button_icon';
        icon.style.cssText = 'background: none; width: 16px; height: 16px; display: inline-block;';
        icon.innerHTML = '⧉';
        icon.style.fontSize = '16px';
        icon.style.lineHeight = '16px';

        const label = document.createElement('span');
        label.className = 'cke_button_label';
        label.textContent = 'Invisible Char';
        label.style.display = 'none';

        btn.appendChild(icon);
        btn.appendChild(label);

        btn.addEventListener('click', (e) => {
            e.preventDefault();
            insertInvisibleChar();
        });

        return btn;
    }

    function addButton() {
        const toolgroup = document.querySelector('.cke_toolgroup');
        if (!toolgroup) {
            setTimeout(addButton, 500);
            return;
        }

        if (document.getElementById('invisible-char-btn')) return;

        const btn = createButton();
        toolgroup.appendChild(btn);
    }

    // Wait for editor to load
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => setTimeout(addButton, 1000));
    } else {
        setTimeout(addButton, 1000);
    }

    // Re-add button if comment widget reloads
    const observer = new MutationObserver(() => {
        if (document.querySelector('.cke_toolgroup') && !document.getElementById('invisible-char-btn')) {
            addButton();
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();