Latexlive公式编辑器 为公式添加 $$ 符号,并修复常见的图片识别结果中的错误

为中文文本中的公式添加 $$ 符号,以适应 Markdown,Latex 格式的需求(尤其匹配 Logseq)。并修复常见的图片识别结果中的错误

目前為 2024-03-30 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Latexlive公式编辑器 为公式添加 $$ 符号,并修复常见的图片识别结果中的错误 
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  为中文文本中的公式添加 $$ 符号,以适应 Markdown,Latex 格式的需求(尤其匹配 Logseq)。并修复常见的图片识别结果中的错误
// @author       Another_Ghost
// @match        https://*.latexlive.com/*
// @icon         https://img.icons8.com/?size=50&id=1759&format=png
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    createButton('复制', copyOriginalText, '');
    createButton('转换后复制', convertFormulasToLaTeX, /\\boldsymbol/g);

    /**
     * 创建按钮并添加到指定容器中
     * @param {number} buttonName - 按钮的名字
     * @param {function} convert - 转换函数
     * @param {string} wordsToRemove - 需要移除的字符串
     */
    function createButton(buttonName, convert, wordsToRemove) {
        // 创建一个新按钮
        let button = document.createElement('button');
        button.innerHTML = `${buttonName}`;
        button.className = 'btn btn-light btn-outline-dark';
        //button.id = `copy-btn${buttonId}`;
        // add click handler
        button.onclick = function () {
            //选中输入文本框的所有文本
            var selected = document.querySelector('#txta_input'); 
            //先通过 convert 函数转换文本,再复制
            navigator.clipboard.writeText(convert(selected.value, wordsToRemove));
            displayAlertBox('已复制');
        };
        //输入框上方的容器
        var CONTAINER = "#wrap_immediate > row > div.col-5.col-sm-5.col-md-5.col-lg-5.col-xl-5";
        //等待容器出现并添加按钮
        var interval = setInterval(function () {
            var wrap = document.querySelector(CONTAINER);
            if (wrap) {
                wrap.appendChild(button);
                clearInterval(interval);
            }
        }, 200);
    }

    function copyOriginalText(inStr, wordsToRemove = '') {
        return inStr;
    }


    let bRadical = true;
/**
 * 将字符串中的公式转换为LaTeX格式,用"$$"包围起来。
 */
function convertFormulasToLaTeX(inStr, wordsToRemove = '') {
    //let str1 = "3) 求序列  R_{4}(n)  的  Z  变换:\n\n\\boldsymbol{X}(\\mathbf{z})=\\sum_{\\boldsymbol{n}=-\\infty}^{\\infty} \\boldsymbol{x}(\\boldsymbol{n}) z^{-n}=\\sum_{\\boldsymbol{n}\n";
    inStr = inStr.trim(); //删除字符串两端的空白字符
    if(bRadical)
    {
        inStr = inStr.replace(/\\begin{array}{[^{}]*}/g, '\\begin{aligned}');
        inStr = inStr.replace(/\\end{array}/g, '\\end{aligned}');
    }
    inStr = inStr.replace(wordsToRemove, '');
    inStr = inStr.replace(/ +/g, ' '); //将多个空格替换为一个空格
    inStr = inStr.replace(/\n+/g, '\n'); //去除重复换行符
    inStr = inStr.replace('输人', '输入'); 
    let str = inStr.trim(); 
    //console.log(inStr);
    let outStr = "";
    let equation = "";
    let bEquation = false;

    //处理并存储已确定的一个公式
    let PushEquationToOutStr = (nextChar) => {
        if(/[\-<=>\\\^_{\|}\/\*\+\(]/.test(equation) || /^[a-zA-Z0-9]$/.test(equation.trim())){
            outStr += ToMarkdown(equation, nextChar);
        }
        else
        {
            outStr+=equation;
        }
        bEquation = false;
        equation = "";
    }

    function ToMarkdown(equation, nextChar)
    {
        equationSymbol = "$";
        let prevChar = outStr[outStr.length-1];
        if((!nextChar || nextChar.match(/[\n\r]/)) && (!prevChar || prevChar.match(/[\n\r]/)))
        {
            equationSymbol = "$$";
        }
        if(equation[equation.length-1] != '$' || equation[0] != '$$')
        {
            equation = insertCharAt(equation, equationSymbol, findLastNonWhitespaceChar(equation)+1); //在公式字符串的最后一个非空格字符的位置的后一个位置插入"$$")
        }
        if(equation[0] != '$' || equation[0] != '$$')
        {
            equation = equationSymbol + equation;
        }
        return equation;
    }
    for(let i = 0; i < str.length; i++) {
        let c = str[i];
        //let nextChar = i < str.length - 1 ? str[i + 1] : '';
        if(!bEquation){
            if(c.match(/[!-~]/)) { //判断是否是非空格ASCII字符
                if(!bEquation && !c.match(/[:,.]/)) //把开头的 ":" 排除在 $$ 外;因为之后一般会跟着换行符,所以没有写在 ToLatex 函数里
                {
                    bEquation = true;
                } 
            }
        }
        else{
            if((c.match(/[\n\r]/) && (!/\\begin/.test(equation) || /\\end/.test(equation))) || (!c.match(/[ -~]/) && !c.match(/[\n\r]/)))
            {
                PushEquationToOutStr(c);
            }
        }

        if(bEquation){
            equation += c;
        }
        else{
            outStr += c;
        }
    }
    if(equation.length > 0) {
        PushEquationToOutStr('');
    }
    console.log(outStr);
    return outStr;

    /**
     * Insert a character at a specified index in the original string.
     * @param {string} originalString - The original string.
     * @param {string} charToInsert - The character to insert.
     * @param {number} index - The index to insert at.
     * @returns {string} - The new string with the inserted character.
     */
    function insertCharAt(originalString, charToInsert, index) {
        let firstPart = originalString.slice(0, index);
        let secondPart = originalString.slice(index);
        return `${firstPart}${charToInsert}${secondPart}`;
    }

    function findLastNonWhitespaceChar(str) {
        const match = str.match(/(\S)\s*$/);
        return match ? str.lastIndexOf(match[1]) : -1;
    }
    
}
})();