remarkjs / remark-math

remark and rehype plugins to support math
https://remark.js.org
MIT License
368 stars 54 forks source link

Add mhchem support to rehype-katex #81

Closed MrWillCom closed 1 year ago

MrWillCom commented 1 year ago

Initial checklist

Problem

I'm using LaTeX to write some docs about Chemistry, and I need mhchem to write equations easily.

Solution

However, rehype-katex is pure, and does not support \ce{} syntax by default. To use mhchem in KaTeX, I need mhchem extension, which is an official extension made by KaTeX team.

Adding <script /> into <head /> directly is not recommended, and doesn't work properly with Next.js built-in router.

Alternatives

Well, switching to rehype-mathjax works, but, #80 happens to me, too.

Related

69 and #34 are both closed and marked as wontfix, a little bit confused, why not?

ChristianMurphy commented 1 year ago

They are closed because mhchem is already supported, so additional changes are not needed. See the response from the issues you linked: https://github.com/remarkjs/remark-math/pull/34#issuecomment-527345050

An updated example looks like:

import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkMath from "remark-math";
import remarkRehype from "remark-rehype";
import rehypeKatex from "rehype-katex";
import rehypeStringify from "rehype-stringify";

import "katex/dist/contrib/mhchem";

const sourceMarkdown = `
$\\ce{a A + b B -> c \\textbf{C} + d D}$
`;

const file = await unified()
  .use(remarkParse)
  .use(remarkMath)
  .use(remarkRehype)
  .use(rehypeKatex)
  .use(rehypeStringify)
  .process(sourceMarkdown);

runnable example: https://codesandbox.io/s/katex-mhchem-wl7q64

github-actions[bot] commented 1 year ago

Hi! This was closed. Team: If this was fixed, please add phase/solved. Otherwise, please add one of the no/* labels.

MrWillCom commented 1 year ago

Thank you so much! Excuse me, but how can I use this with Next.js?

I've tried to add katex/dist/contrib/mhchem with CDN, like this:

import Script from 'next/script'

...

<Script src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/mhchem.min.js" />

However, \ce{} doesn't work and an error is thrown in the console:

screenshot of the error

Thanks again for your patience, do you have any idea with this?

ChristianMurphy commented 1 year ago

Could you share what you are trying to do in a sandbox? Using https://next.new as a starter? There are a bunch of different integrations with Next for different use cases, it's unclear which you are using. Also this may be better suited as a Q&A discussion: https://github.com/orgs/remarkjs/discussions

philipbalbas commented 1 year ago

Hi, I have a similar issue where I'm importing mhchem to a Nextjs project but the equations aren't rendering correctly.

https://stackblitz.com/edit/nextjs-idj33f?file=pages%2Fmath.js,pages%2Findex.js,pages%2F_app.js

ChristianMurphy commented 1 year ago

Thanks @philipbalbas!

Given that https://codesandbox.io/s/katex-mhchem-wl7q64 works in browser I suspect there is an issue where the plugin isn't able to register in node/next

ChristianMurphy commented 1 year ago

Taking that further, I tried to isolate the issue a bit more. I ended up with https://stackblitz.com/edit/nextjs-xvqkxb?file=pages/index.js It feels like next is isolating or messing with the katex context, I'm not sure, the next team may be able to offer more commentary on what next is doing. https://github.com/vercel/next.js/discussions

philipbalbas commented 1 year ago

Hi, thanks for taking a look. Noted on the nextjs issues. I also suspect mdxjs not getting the context of mhchem but will have to investigate further.

ChristianMurphy commented 1 year ago

I also suspect mdxjs not getting the context of mhchem but will have to investigate further.

That is the side effect, yes. It doesn't necessarily get at the "why?" Given https://codesandbox.io/s/katex-mhchem-wl7q64 and https://stackblitz.com/edit/nextjs-xvqkxb?file=pages/index.js clearly rehype-katex clearly can access the mhchem context. With the smaller reproducible examples it's increasingly looking like it works up to the point next it added to the picture. Which implies something next is doing is likely the root cause.

Combined with https://github.com/remarkjs/remark-math/issues/80 it leads me to believe something about Next SSR messes with Katex and Mathjax. Remark and MDX just happen to also be in the dependency tree, but seem to be unrelated to the issues being seen.

edward1127 commented 1 year ago

I'm getting the same issue to make mhchem work with nextjs. Have anyone solved the issue?

import "katex/dist/katex.min.css";
import "katex/dist/contrib/mhchem";
import rendererStyle from "@/styles/quesiton-renderer.module.scss";
import React, { useMemo } from "react";
import remarkMath from "remark-math";
import remarkGfm from "remark-gfm";
import rehypeKatex from "rehype-katex";
import ReactMarkdown from "react-markdown";
import remarkDirective from "remark-directive";
import rehypeStringify from "rehype-stringify";
import remarkDirectiveRehype from "remark-directive-rehype";
import MathField from "./Control/MathField";
import type { IQuestion } from "@/interfaces/problems";
import TextInput from "./Control/TextInput";
import Checkbox from "./Control/Checkbox";
import TextArea from "./Control/Editor";
import Editor from "./Control/Editor";
import Radio from "./Control/Radio";
import { environment } from "@/lib/environment";
import type { FormValues } from "../Form/ProblemForm";
import SketchCanvas from "./Control/SketchCanvas";
import Matching, { MatchField } from "./Control/Matching";
import CSSInJs from "./CSSInJs";
import { cn } from "@/lib/style";

function QuestionRender({
  question,
  isEditMode = false,
  className,
}: {
  question: Partial<IQuestion> | FormValues;
  isEditMode?: boolean;
  className?: string;
}) {
  const components = useMemo(() => {
    return {
      "math-input": (props: any) => (
        <MathField isEditMode={isEditMode} question={question} {...props} />
      ),
      "text-area": (props: any) => (
        <TextArea isEditMode={isEditMode} question={question} {...props} />
      ),
      "text-input": (props: any) => (
        <TextInput isEditMode={isEditMode} question={question} {...props} />
      ),
      "match-field": (props: any) => (
        <MatchField isEditMode={isEditMode} question={question} {...props} />
      ),
      matching: (props: any) => (
        <Matching isEditMode={isEditMode} question={question} {...props} />
      ),
      canvas: (props: any) => (
        <SketchCanvas isEditMode={isEditMode} question={question} {...props} />
      ),
      editor: (props: any) => (
        <Editor isEditMode={isEditMode} question={question} {...props} />
      ),
      radio: (props: any) => (
        <Radio isEditMode={isEditMode} question={question} {...props} />
      ),
      checkbox: (props: any) => (
        <Checkbox isEditMode={isEditMode} question={question} {...props} />
      ),
      p: (props: any) => <>{props.children}</>,
      img: ({ className, ...props }: { className: string; props: any }) => (
        <img
          className={`w-auto lg:max-w-[50%] max-h-96 ${
            className ? className : ""
          }`}
          {...props}
        />
      ),
      ins: ({ className, ...props }: any) => (
        <span className={cn("underline", props.className)} {...props}>
          {props.children}
        </span>
      ),
      box: (props: any) => (
        <div className={cn("border-2 border-black p-3", props.className)}>
          {props.children}
        </div>
      ),
      blank: ({ className, ...props }: any) => (
        <span
          className={cn("border-b-2 px-5 border-black", className)}
          {...props}
        >
          {props.value || props.children}
        </span>
      ),
      table: ({ className, ...props }: { className: string; props: any }) => (
        <table
          className={`table-fixed my-3 ${className ? className : ""}`}
          {...props}
        />
      ),
      td: ({
        className,
        isHeader,
        ...props
      }: {
        className: string;
        isHeader: boolean;
        props: any;
      }) => {
        return (
          <td
            className={`align-middle ${className ? className : ""}`}
            {...props}
          />
        );
      },
    };
  }, [question]);

  return (
    <>
      <ReactMarkdown
        className={cn(
          `max-w-none dynamic-tailwind prose leading-10 text-black ${rendererStyle["question-render"]}`,
          className
        )}
        remarkPlugins={[
          remarkMath,
          remarkGfm,
          remarkDirective,
          remarkDirectiveRehype,
        ]}
        rehypePlugins={[rehypeKatex, rehypeStringify]}
        components={components}
        transformImageUri={(src, alt, title) => {
          return `${environment.staticUrl}/${src}`;
        }}
      >
        {question.description}
      </ReactMarkdown>
      {isEditMode && <CSSInJs description={question.description} />}
    </>
  );
}

export default React.memo(QuestionRender);
ChristianMurphy commented 1 year ago

Welcome @edward1127! 👋 Did you get a chance to read my message right before yours? In particular:

Combined with https://github.com/remarkjs/remark-math/issues/80 it leads me to believe something about Next SSR messes with Katex and Mathjax. Remark and MDX just happen to also be in the dependency tree, but seem to be unrelated to the issues being seen.

This doesn't appear to be a remark/mdx issue, so there isn't necessarily a remark/mdx workaround to be offered. It may be a good idea to float this question to the Next.js team (https://github.com/vercel/next.js/discussions), then cross-post the link to the Next.js discussion here so that it can be more easily found by others.

edward1127 commented 1 year ago

Thanks. I just reposted your summary to the Next.js team https://github.com/vercel/next.js/discussions/54852

edward1127 commented 1 year ago

I tried to use CSR with Next https://stackblitz.com/edit/nextjs-ftepsg?file=pages%2Findex.js, and it's still not working. I believe somehow nextjs cannot get the context of import "katex/dist/contrib/mhchem";

edward1127 commented 10 months ago

If explicitly import the mhchem extension, it will work for nextjs https://github.com/remarkjs/remark-math/pull/92