Untitled

 avatar
unknown
plain_text
2 days ago
4.9 kB
60
Indexable
const { Plugin } = require("obsidian");

module.exports = class VimTerminalCaretPlugin extends Plugin {
    async onload() {
        // =========================
        // CARET ELEMENT (BLOCK CURSOR)
        // =========================
        this.caret = document.createElement("div");
        Object.assign(this.caret.style, {
            position: "fixed",
            height: "1.2em",
            background: "#e0e0e0",
            mixBlendMode: "difference",
            pointerEvents: "none",
            zIndex: "999999",
            opacity: "0.9",
            boxSizing: "border-box",
            borderRadius: "2px"
        });
        document.body.appendChild(this.caret);

        // =========================
        // DYNAMIC CHAR WIDTH
        // =========================
        this.getCharWidth = () => {
            const sel = window.getSelection();
            if (!sel || sel.rangeCount === 0) {
                // fallback
                const editorEl = document.querySelector(".cm-content") || document.body;
                const test = document.createElement("div");
                test.textContent = "MMMMMMMMMM";
                test.style.position = "absolute";
                test.style.visibility = "hidden";
                test.style.whiteSpace = "pre";
                const style = getComputedStyle(editorEl);
                test.style.fontFamily = style.fontFamily;
                test.style.fontSize = style.fontSize;
                test.style.fontWeight = style.fontWeight;
                test.style.letterSpacing = style.letterSpacing;
                document.body.appendChild(test);
                const width = test.getBoundingClientRect().width / 10;
                test.remove();
                return width;
            }

            const range = sel.getRangeAt(0);
            let element = range.startContainer.nodeType === 3
                ? range.startContainer.parentElement
                : range.startContainer;

            while (element && !element.classList?.contains("cm-line") && element.parentElement) {
                element = element.parentElement;
            }

            const test = document.createElement("div");
            test.textContent = "M";
            test.style.position = "absolute";
            test.style.visibility = "hidden";
            test.style.whiteSpace = "pre";
            test.style.display = "inline-block";

            if (element) {
                const style = getComputedStyle(element);
                test.style.fontFamily = style.fontFamily;
                test.style.fontSize = style.fontSize;
                test.style.fontWeight = style.fontWeight;
                test.style.letterSpacing = style.letterSpacing;
            }

            document.body.appendChild(test);
            const width = test.getBoundingClientRect().width;
            test.remove();
            return width;
        };

        let charWidth = this.getCharWidth();

        // =========================
        // CARET UPDATE
        // =========================
        const updateCaretPosition = () => {
            const sel = window.getSelection();
            if (!sel || sel.rangeCount === 0) return;

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

            // FIX: EMPTY LINE SUPPORT
            if (!rect || (rect.width === 0 && rect.height === 0)) {
                let node = range.startContainer;
                if (node.nodeType === 3) node = node.parentElement;
                while (node && !node.classList?.contains("cm-line")) {
                    node = node.parentElement;
                }
                if (!node) return;

                const lineRect = node.getBoundingClientRect();
                rect = {
                    left: lineRect.left,
                    top: lineRect.top,
                    height: lineRect.height,
                    width: 1
                };
            }

            charWidth = this.getCharWidth();

            this.caret.style.left = rect.left + "px";
            this.caret.style.top = rect.top + "px";
            this.caret.style.width = (charWidth + 1) + "px";
            this.caret.style.height = rect.height + "px";
        };

        // =========================
        // EVENTS
        // =========================
        document.addEventListener("keydown", updateCaretPosition);
        document.addEventListener("keyup", updateCaretPosition);
        document.addEventListener("mouseup", updateCaretPosition);
        
        // Scroll: update position only
        document.addEventListener("scroll", updateCaretPosition, true);
    }

    onunload() {
        if (this.caret) this.caret.remove();
    }
};
Editor is loading...
Leave a Comment