Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally Support rendered inline math using $$. #3225

Open
a1473838623 opened this issue Feb 5, 2024 · 0 comments
Open

Optionally Support rendered inline math using $$. #3225

a1473838623 opened this issue Feb 5, 2024 · 0 comments
Labels
Enhancement Enhance performance or improve usability of original features. Need Discussion Need discussion or investigation

Comments

@a1473838623
Copy link

a1473838623 commented Feb 5, 2024

Version

Latest

Development Environment

OS

Current Behavior

$$ line math not working. we can using code block to custom latex render, but code block is not a very popular way to render latex which make it difficult to change editor.

$$latex
\sin x
$$

And my current solution is render from text, which makes my solution become very limited:
My solution support Katex、Latex math formula using $$ (some has been tested, not every formula), and this solution is limited because you need press "format change" button for the first time(or you can just notice not to use special formula like { 、}、\、*、, and so on).

Despite it is not perfect, I can use this solution to cover my old Latex formula. And you can judge if to use this solution or not by checking these pictures.

Really hope ui.editor can support Latex someday.

tui.editor:
image

other md editor:
image

import { getLocale } from '@umijs/max';
import { Button } from 'antd';
import { useRef} from 'react';
import { Editor } from '@toast-ui/react-editor';
//@ts-ignore
import katex from 'katex';
import { MdNode } from '@toast-ui/editor';

const EditBlog = () => {
  const editorRef = useRef(null);
  const lang = getLocale().toString();

  function renderMathOnLeft(str: string) {
    const html = katex.renderToString(str, {
      throwOnError: false,
      displayMode: false,
      output: "mathml",
      globalGroup: true,
      fleqn: true,
    });
    return html;
  }

  // function renderMathOnCenter(str: string) {
  //   const html = katex.renderToString(str, {
  //     throwOnError: false,
  //     displayMode: false,
  //     output: "mathml",
  //     globalGroup: true,
  //     // this config can render math on center
  //     fleqn: false
  //   });
  //   return html;
  // }

  let latexPart: string = "";
  let ifEntering: boolean = false;
  let ifEven = true;
  let lastUncloseNode: MdNode | null = null;
  function getInlineMath(node: MdNode) {
    let noNeedNewLine = false;
    let str = node?.literal || "";
    let prevIdx = 0;
    let nextIdx = -1;
    let ifLastNode = (node?.next === null);
    let count = 0;
    if (node !== null && node === node?.parent?.firstChild) {
      let n: MdNode | null = node;
      while (n !== null) {
        for (let i = 0; i < (n?.literal?.length ?? 0); i++) {
          if (n?.literal?.[i] === "$") {
            count++;
          }
        }
        if (count % 2 === 1) {
          lastUncloseNode = n;
        } else {
          lastUncloseNode = null;
        }
        n = n?.next;
      }
      ifEven = (count % 2 === 0);
    }
    while (true) {
      prevIdx = str.indexOf("$", prevIdx);
      nextIdx = str.indexOf("$", prevIdx + 1);
      if (ifLastNode && !ifEntering && (prevIdx === -1 || nextIdx === -1)) {
        ifEntering = false;
        latexPart = '';
        break;
      }
      if (ifLastNode && ifEntering && prevIdx === -1) {
        ifEntering = false;
        latexPart = '';
        break;
      }
      if (ifEntering && prevIdx !== -1) {
        latexPart += ' ' + str.slice(0, prevIdx);
        str = str.replace(str.slice(0, prevIdx + 1), renderMathOnLeft(latexPart));
        latexPart = '';
        ifEntering = false;
        prevIdx += 1;
        continue;
      }
      if (!ifEntering && prevIdx !== -1 && nextIdx !== -1) {
        str = str.replace(str.slice(prevIdx, nextIdx + 1), renderMathOnLeft(str.slice(prevIdx + 1, nextIdx)));
        prevIdx = nextIdx + 1;
        continue;
      }
      if (!ifLastNode && !ifEntering && prevIdx === -1) {
        break;
      }
      if (!ifLastNode && !ifEntering && prevIdx !== -1 && nextIdx === -1) {
        ifEntering = true;
        latexPart = str.slice(prevIdx + 1) + ' ';
        if (ifEven || !Object.is(node, lastUncloseNode)) {
          str = str.replace(str.slice(prevIdx), '');
          noNeedNewLine = true;
        }
        break;
      }
      if (!ifLastNode && ifEntering && prevIdx === -1) {
        latexPart += ' ' + str.slice(0) + ' ';
        if (ifEven || !Object.is(node, lastUncloseNode)) {
          str = '';
          noNeedNewLine = true;
        }
        break;
      }
    }
    if (noNeedNewLine) {
      node.literal = "$";
    }
    return str;
  }

  const latexFilter = (str: string) => {
    return str?.replaceAll("\\\\", " \\newline ").replaceAll("\\{", " \\lbrace ").replaceAll("\\}", " \\rbrace ").replaceAll("*", " \\ast ").replaceAll("\\,", " \\thinspace ").replaceAll("_", " _ ").replaceAll("[", " \\lbrack ").replaceAll("]", " \\rbrack ");
  }

  const formatLatex = () => {
    // @ts-ignore
    const ed = editorRef?.current?.getInstance();
    let md = ed?.getMarkdown()?.replaceAll("$$=$$", "=");
    let mdCopy: string = md;
    let prevIdx = 0;
    let nextIdx = -1;
    while (true) {
      prevIdx = md.indexOf("$", prevIdx);
      nextIdx = md.indexOf("$", prevIdx + 1);
      if (prevIdx === -1) {
        break;
      } else if (nextIdx !== -1) {
        let partMd = md?.slice(prevIdx, nextIdx + 1);
        let tempMd = "$" + md?.slice(prevIdx + 1, nextIdx)?.trim() + "$";
        let partMdReplace = latexFilter(tempMd);
        mdCopy = mdCopy?.replace(partMd, partMdReplace);
        prevIdx = nextIdx + 1;
      } else {
        let partMd = md?.slice(prevIdx);
        let tempMd: string = "$" + md?.slice(prevIdx + 1)?.trim();
        let partMdReplace = latexFilter(tempMd);
        mdCopy = mdCopy?.replace(partMd, partMdReplace);
        break;
      }
    }
    ed?.setMarkdown(mdCopy);
  }
  return (
    <>
      <Button onClick={formatLatex}>format latex once(only once!!!)</Button>
      <Editor
        initialValue={`$sin
        x$`}
        previewStyle="vertical"
        height="600px"
        initialEditType="markdown"
        useCommandShortcut={true}
        usageStatistics={false}
        language={lang}
        hideModeSwitch={true}
        customHTMLRenderer={{
          text(node) {
            const str = getInlineMath(node);
            const isNodeHTML = (str?.indexOf("<span class=\"katex\">") !== -1);
            return {
              type: isNodeHTML ? 'html' : 'text',
              content: str
            }
          },
          softbreak(node: MdNode) {
            const isPrevNodeHTML = node.prev && node.prev.type === 'htmlInline';
            const isPrevBR = isPrevNodeHTML && /<br ?\/?>/.test(node.prev!.literal!);
            let str = undefined;
            if (node?.prev !== null) {
              str = node?.prev?.literal;
            }
            const content = (str === "$" ? "" : (isPrevBR ? '\n' : '<br>\n'));
            return { type: isPrevBR ? 'text' : 'html', content };
          },
        }}
        ref={editorRef}
      />
    </>
  );
};

export default EditBlog;

Expected Behavior

other md editor:
image

Please just make Latex an option, that would not confuse those people who don't use Latex.

@a1473838623 a1473838623 added Enhancement Enhance performance or improve usability of original features. Need Discussion Need discussion or investigation labels Feb 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement Enhance performance or improve usability of original features. Need Discussion Need discussion or investigation
Projects
None yet
Development

No branches or pull requests

1 participant