Reader view

Reader Mode + Highlight with resilient floating button for tricky sites

Verzia zo dňa 18.05.2026. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         Reader view
// @namespace    http://tampermonkey.net/
// @version      16.10.2025
// @description  Reader Mode + Highlight with resilient floating button for tricky sites
// @author       Copilot
// @match        *://*/*
// @grant        GM_registerMenuCommand
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    let readerOpened = false; // flag to prevent reinjection after Reader Mode

    // ✅ Menu command added
    GM_registerMenuCommand("Open Reader Mode", () => {
        const btn = document.getElementById('tm-reader-btn');
        if (btn) {
            btn.click();
        } else {
            alert("Reader button not available yet.");
        }
    });

    function injectButton() {
        if (readerOpened) return; // stop reinjecting once Reader Mode is active
        if (document.getElementById('tm-reader-box')) return;

        const readerBox = document.createElement('div');
        readerBox.id = 'tm-reader-box';
        const readerBtn = document.createElement('button');
        readerBtn.id = 'tm-reader-btn';
        readerBtn.textContent = 'Reader';
        readerBox.appendChild(readerBtn);

        // Strong CSS
        const style = document.createElement('style');
        style.textContent = `
            #tm-reader-box {
                position: fixed !important;
                bottom: 10px !important;
                right: 60px !important;
                padding: 2px 6px !important;
                background: transparent !important;
                border: 1px solid transparent !important;
                border-radius: 6px !important;
                z-index: 2147483647 !important;
            }
            #tm-reader-btn {
                border: none !important;
                background: transparent !important;
                color: #696969 !important;
                font-size: 12px !important;
                cursor: pointer !important;
                font-family: system-ui, sans-serif !important;

                padding: 0 !important;
                margin: 0 !important;
            }
            #tm-reader-btn:hover { text-decoration: underline !important; }
        `;
        document.head.appendChild(style);

        // Append to both html and body
        if (document.body) document.body.appendChild(readerBox);
        document.documentElement.appendChild(readerBox);

        // Reader Mode logic
        readerBtn.addEventListener('click', async function() {
            const loadScript = url => new Promise((resolve, reject) => {
                const script = document.createElement('script');
                script.src = url;
                script.onload = resolve;
                script.onerror = reject;
                document.head.appendChild(script);
            });

            try {
                if (!window.Readability) {
                    await loadScript('https://cdn.jsdelivr.net/npm/@mozilla/[email protected]/Readability.js');
                }
                if (!window.DOMPurify) {
                    await loadScript('https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js');
                }

                const clone = document.cloneNode(true);
                const article = new Readability(clone).parse();
                if (!article) {
                    alert('Reader Mode: No article found on this page.');
                    return;
                }

                // Remove the floating Reader button completely
                const readerBox = document.getElementById('tm-reader-box');
                if (readerBox) readerBox.remove();
                readerOpened = true; // prevent reinjection

                const css = `
                    html, body { margin:0; padding:0; background:#fff; }
                    body {
                        font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif !important;
                        max-width: 48rem;
                        margin: 0 auto;
                        padding: 2rem;
                        color: #111;
                        line-height: 1.6;
                    }
                    article *, article {
                        font-size: 28px !important;
                        font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif !important;
                    }
                    p, div, span, li { margin-bottom:1.5rem; word-break:normal; overflow-wrap:break-word; }
                    h1,h2,h3,h4,h5,h6 { line-height:1.3; margin:1.5rem 0 0.5rem; font-weight:700; }
                    a { color:#0b57d0; text-decoration:none; cursor:pointer; }
                    a:hover { text-decoration:underline; }
                    img, video { max-width:100%; height:auto; border-radius:8px; display:block; margin:2rem auto; }
                    pre, code { font-family: ui-monospace, monospace !important; font-size:22px !important; background:#f5f5f5; padding:0.2em 0.4em; border-radius:3px; }
                    @media (prefers-color-scheme: dark) {
                        html, body { background:#111; }
                        body { color:#eee; }
                        pre, code { background:#222; }
                        a { color:#4a90e2; }
                    }
                `;

                const html = `
                    <!DOCTYPE html>
                    <html>
                    <head>
                        <meta charset="utf-8">
                        <meta name="viewport" content="width=device-width, initial-scale=1.0">
                        <title>${article.title || 'Reader Mode'}</title>
                        <style>${css}</style>
                    </head>
                    <body>
                        <article>
                            <h1>${article.title || ''}</h1>
                            <div>${window.DOMPurify ? DOMPurify.sanitize(article.content) : article.content}</div>
                        </article>
                    </body>
                    </html>
                `;

                document.open();
                document.write(html);
                document.close();

                // Highlight button
                const btn = document.createElement('button');
                btn.innerText = '🖍️';
                btn.id = 'tm-highlight-btn';
                btn.style.cssText = `
                    position: fixed;
                    bottom: 20px;
                    right: 20px;
                    z-index: 10000;
                    padding: 10px;
                    border-radius: 50%;
                    background-color: #ffeb3b;
                    border: 2px solid #000;
                    font-size: 20px;
                    display: none;
                `;
                document.body.appendChild(btn);

                document.addEventListener('selectionchange', () => {
                    const selection = window.getSelection();
                    btn.style.display = (selection.toString().length > 0) ? 'block' : 'none';
                });

                btn.addEventListener('click', () => {
                    const selection = window.getSelection();
                    if (!selection.rangeCount) return;
                    const range = selection.getRangeAt(0);
                    const mark = document.createElement('mark');
                    mark.style.backgroundColor = 'yellow';
                    mark.style.color = 'black';
                    try {
                        range.surroundContents(mark);
                        selection.removeAllRanges();
                        btn.style.display = 'none';
                    } catch (e) {
                        alert("Can't highlight across multiple complex elements. Try a smaller selection!");
                    }
                });
            } catch (err) {
                alert('Reader Mode failed: ' + err.message);
            }
        });
    }

    // Inject after load
    window.addEventListener('load', injectButton);

    // Reinjection checks
    const observer = new MutationObserver(() => injectButton());
    observer.observe(document.documentElement, { childList: true, subtree: true });
    setInterval(injectButton, 2000);
})();