DEV Community

Kyle
Kyle

Posted on

I forked MathLive to make basic chemistry formulas less awkward

I recently published a small MathLive fork:

GitHub:

https://github.com/LatoAndroid/mathlive-chemistry

npm:

https://www.npmjs.com/package/mathlive-chemistry

It is not the official MathLive package. It is a focused fork of arnog/mathlive that adds a bit of chemistry support:

  1. better editing behavior for common \ce{...} formulas;
  2. a small \chemfig{...} subset for simple organic structures.

Most of the coding for this fork was done with AI coding assistance. Not in the "one prompt and ship it" way. I set the scope, read the existing code, pushed back on bad changes, checked the rendering details, and used AI mostly as the implementation/debugging partner. That worked well for this kind of fork because the important part was not inventing a new architecture, but keeping the changes small and compatible.

The goal is not to build a professional chemistry editor. It is more boring than that: make common chemistry notation behave reasonably inside a MathLive-based formula editor.

Why I forked it

MathLive already does the hard parts of math editing well: rendering, selection, serialization, keyboard input, virtual keyboard support, and so on.

Chemistry is the rough edge.

Upstream MathLive already supports part of mhchem:

\ce{2H2 + O2 -> 2H2O}
\ce{CaCO3 ->[heat] CaO + CO2 ^}
\ce{SO4^2- + Ba^2+ -> BaSO4 v}
Enter fullscreen mode Exit fullscreen mode

But \ce{...} is mostly display-oriented. Internally it is represented by ChemAtom. The rough flow is:

\ce{...}
  -> read the argument as a balanced string
  -> parse it with the mhchem parser
  -> texify it into normal TeX
  -> parseLatex(tex)
  -> render through MathLive
Enter fullscreen mode Exit fullscreen mode

That is a good design for display. It preserves the original \ce{...} source and avoids guessing too much.

The problem is editing. In some cases the formula still behaves too much like one captured object instead of something that can participate naturally in MathLive editing.

What I changed for ChemAtom

I did not make every \ce editable. That would be unsafe. mhchem accepts a lot of syntax, and it can contain embedded TeX. If the fork tries to normalize all of that, it will eventually damage somebody's input.

So the editable path is intentionally narrow:

  • only \ce, not \pu;
  • skip empty or very long arguments;
  • skip arguments containing \, $, or &;
  • inspect the mhchem parser output and allow only known simple output types.

The check is roughly:

function isEditableChemFormula(command, arg, parsed) {
  if (command !== '\\ce') return false;
  if (!arg || arg.length > 512) return false;
  if (/[\\$&]/.test(arg)) return false;
  return isEditableChemOutput(parsed);
}
Enter fullscreen mode Exit fullscreen mode

For the editable subset, captureSelection is disabled so the generated MathLive body can be selected and edited recursively.

Serialization is the other important part. The atom does not just keep returning the original string. It serializes the current body, runs a small normalization step, then wraps it back into:

\ce{...}
Enter fullscreen mode Exit fullscreen mode

For example, a test changes the internal body from:

\ce{2H2 + O2 -> 2H2O}
Enter fullscreen mode Exit fullscreen mode

to something like:

3\mathrm{H}_{2}+\mathrm{O}_{2}\longrightarrow2\mathrm{H}_{2}\mathrm{O}
Enter fullscreen mode Exit fullscreen mode

and gets this back:

\ce{3H2+O2 -> 2H2O}
Enter fullscreen mode Exit fullscreen mode

It is not trying to preserve every space. It is trying to preserve a valid editable chemistry formula.

The chemfig part

MathLive does not support \chemfig upstream. A command like this is unknown:

\chemfig{*6(-=-=-=)}
Enter fullscreen mode Exit fullscreen mode

Full chemfig support would be a much bigger project. I only needed a few common structures:

\chemfig{CH_3-CH_2-OH}
\chemfig{CH_2=CH_2}
\chemfig{HC#CH}
\chemfig{CH_3-C(=O)-OH}
\chemfig{*6(-=-=-=)}
\chemfig{*6(-=-(-CH_3)-=-)}
\chemfig{*6(-=-(-NO_2)-=-)}
\chemfig{*6(-=-(-COOH)-=-)}
Enter fullscreen mode Exit fullscreen mode

So this fork uses a small parser instead of a full chemfig implementation:

  • parseLinear() for simple chains and single/double/triple bonds;
  • parseRing() for a six-membered ring with simple substituents.

The result is rendered as SVG and then wrapped in a MathLive Box:

\chemfig{...}
  -> parseChemfig()
  -> ChemfigStructure
  -> SVG
  -> Box(width / height / depth)
  -> inline MathLive rendering
Enter fullscreen mode Exit fullscreen mode

This is not the most elegant thing in the world, but it keeps the change local. I did not want to rework MathLive's layout model just for a small chemistry subset.

Right now \chemfig{...} is still an object-level atom for editing. It renders, selects, deletes, and serializes. It is not a visual structure editor.

Install

npm install mathlive-chemistry
Enter fullscreen mode Exit fullscreen mode

Usage stays close to MathLive:

import 'mathlive-chemistry';
Enter fullscreen mode Exit fullscreen mode

or:

import { MathfieldElement } from 'mathlive-chemistry';
Enter fullscreen mode Exit fullscreen mode

What this is not

This fork is not:

  • official MathLive;
  • a full mhchem implementation;
  • a full chemfig implementation;
  • a professional chemistry drawing tool.

It does not handle advanced stereochemistry, wedge/dashed bonds, mechanism arrows, electron pushing, or complex research-grade structures.

Complex \ce{...} and \pu{...} still stay conservative and behave as whole objects. That is intentional.

Relationship to MathLive

Original project:

https://github.com/arnog/mathlive

This fork:

https://github.com/LatoAndroid/mathlive-chemistry

The original project is by Arno Gourdol and MathLive contributors, under the MIT license. This fork keeps the same license.

There is still plenty to polish, especially around spacing, baselines, and the exact chemfig subset. Those tiny layout details are annoying: a one-pixel difference can look fine in isolation and wrong inside a formula.

If you are using MathLive and have real chemistry examples that fail, feel free to open an issue:

https://github.com/LatoAndroid/mathlive-chemistry/issues

Top comments (0)