// ==UserScript==
// @name         Caret Read Progress
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Moves caret with arrow keys, highlights read text, works across paragraphs and divs
// @author       You
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    let highlightDiv = document.createElement("div");
    highlightDiv.style.position = "absolute";
    highlightDiv.style.background = "rgba(255, 255, 0, 0.3)"; // Yellow highlight
    highlightDiv.style.height = "1.5em";
    highlightDiv.style.pointerEvents = "none";
    highlightDiv.style.zIndex = "9999";
    highlightDiv.style.width = "0px"; // Start with no highlight
    document.body.appendChild(highlightDiv);

    document.addEventListener("keydown", function (event) {
        if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;

        let sel = window.getSelection();
        if (!sel.rangeCount) return;

        let range = sel.getRangeAt(0);
        let moved = moveCaret(event.key === "ArrowRight" ? 1 : -1);

        if (moved) updateHighlight();
    });

    function moveCaret(direction) {
        let sel = window.getSelection();
        if (!sel.rangeCount) return false;

        let range = sel.getRangeAt(0);
        let node = range.startContainer;
        let offset = range.startOffset;

        // Move within the same text node
        if (direction === 1 && offset < node.length) {
            offset++;
        } else if (direction === -1 && offset > 0) {
            offset--;
        }
        // If at the end of a node, move to the next text node
        else if (direction === 1) {
            let nextNode = findNextTextNode(node);
            if (nextNode) {
                node = nextNode;
                offset = 0;
            } else {
                return false;
            }
        }
        // If at the start of a node, move to the previous text node
        else if (direction === -1) {
            let prevNode = findPreviousTextNode(node);
            if (prevNode) {
                node = prevNode;
                offset = prevNode.length;
            } else {
                return false;
            }
        }

        // Update selection range
        let newRange = document.createRange();
        newRange.setStart(node, offset);
        newRange.collapse(true);
        sel.removeAllRanges();
        sel.addRange(newRange);

        return true;
    }

    function findNextTextNode(node) {
        while (node) {
            if (node.nextSibling) {
                node = node.nextSibling;
                while (node && node.nodeType !== Node.TEXT_NODE) {
                    node = node.firstChild || node.nextSibling;
                }
                return node;
            }
            node = node.parentNode;
        }
        return null;
    }

    function findPreviousTextNode(node) {
        while (node) {
            if (node.previousSibling) {
                node = node.previousSibling;
                while (node && node.nodeType !== Node.TEXT_NODE) {
                    node = node.lastChild || node.previousSibling;
                }
                return node;
            }
            node = node.parentNode;
        }
        return null;
    }

    function updateHighlight() {
        let sel = window.getSelection();
        if (!sel.rangeCount) return;

        let range = sel.getRangeAt(0);
        let rect = range.getBoundingClientRect();

        if (rect.width === 0 && rect.height === 0) {
            return; // Ignore empty selections
        }

        highlightDiv.style.top = `${rect.top + window.scrollY}px`;
        highlightDiv.style.left = `0px`; // Always start from the left
        highlightDiv.style.width = `${rect.right}px`; // Expand highlight only to where you've read
    }
})();