Stepik Invisible Character Button

Adds invisible character button to Stepik comment editor

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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
    });
})();