Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
bec2cd6
Add native Typst math output format (LaTeX/MathML → Typst)
OlgaRedozubova Mar 10, 2026
155b8a1
Deduplicate thousand-separator logic and address PR review fixes
OlgaRedozubova Mar 10, 2026
7a16211
Tighten isDerivativePattern, deduplicate SCRIPT_NODE_KINDS, fix post-…
OlgaRedozubova Mar 10, 2026
64b9939
Fix asymmetric and mismatched delimiters in matrix/array output
OlgaRedozubova Mar 10, 2026
4a94cf7
Handle \not negation in Typst output via cancel()
OlgaRedozubova Mar 11, 2026
f673fb3
Re-build
OlgaRedozubova Mar 11, 2026
6467224
Escape unpaired brackets in Typst function arguments and unify bracke…
OlgaRedozubova Mar 16, 2026
e7bb7cb
Fix \left.\right] with multiple children: keep all content inside one…
OlgaRedozubova Mar 16, 2026
f06a7da
Add space before ( after multi-char symbol names to prevent function-…
OlgaRedozubova Mar 16, 2026
f0a90a8
Escape colons in function-call arguments to prevent named-argument pa…
OlgaRedozubova Mar 16, 2026
e35455f
Fix false ‖…‖ pairing in bare delimiter scanner and escape func-call …
OlgaRedozubova Mar 16, 2026
b76fb19
Convert nested aligned/gathered inside mat()/cases() cells to mat() w…
OlgaRedozubova Mar 16, 2026
60bdfa4
Add display() for nested matrices/cases, propagate typst_inline throu…
OlgaRedozubova Mar 16, 2026
6764f64
Add scope boundaries to bracket pairing for msqrt, mroot, mfrac, menc…
OlgaRedozubova Mar 16, 2026
c88a7c1
Add reverse cases, hasTableFirst path, and inline tracking for table …
OlgaRedozubova Mar 16, 2026
d2fb002
Fix function-call paren heuristic in bracket scanner
OlgaRedozubova Mar 17, 2026
5297070
Escape colons inside lr() to prevent named-argument parsing
OlgaRedozubova Mar 17, 2026
c4548d9
Group consecutive non-Latin mi nodes to preserve combining characters
OlgaRedozubova Mar 17, 2026
c958074
Fix limits() placement for \underset/\overset with overline/underline…
OlgaRedozubova Mar 17, 2026
aa849c3
Escape inner brackets inside lr() and fix limits() for underset/overset
OlgaRedozubova Mar 17, 2026
561a35e
Add missing symbol mappings: integral.surf, slash.o, lt.approx, gt.ap…
OlgaRedozubova Mar 17, 2026
f9c6a76
Center #box and #circle in block mode, add missing symbol mappings
OlgaRedozubova Mar 17, 2026
99924ec
Fix unbalanced paren escaping in accent/script handlers
OlgaRedozubova Mar 17, 2026
3d9327b
Use lr(\) ...) for longdiv/lcm to stretch delimiter to content height
OlgaRedozubova Mar 17, 2026
38c1d4b
Handle \xcancel cross mode, escape unbalanced parens in script grouping
OlgaRedozubova Mar 17, 2026
aeb6267
Gate mover/munder accent logic on accent/accentunder attributes
OlgaRedozubova Mar 17, 2026
81c5419
Collapse constructed long arrows and flatten nested mover/munder
OlgaRedozubova Mar 17, 2026
18c4210
Selective box strokes for array borders, cap vline indices, word-safe…
OlgaRedozubova Mar 17, 2026
5acbfc0
Fix missing space between scripted node and ( inside bare-delimiter p…
OlgaRedozubova Mar 17, 2026
a429286
Gathered inside eqnArray: mat()-ify only when gathered has sibling co…
OlgaRedozubova Mar 17, 2026
347b820
Update PR spec: document all post-squash fixes and new features
OlgaRedozubova Mar 18, 2026
bbf403f
Add \Varangle → angle.spheric via custom-cmd-map dispatch
OlgaRedozubova Mar 18, 2026
45bd489
Add symbol mappings, llbracket/rrbracket custom-cmd, delimiter guard
OlgaRedozubova Mar 18, 2026
aa25bec
Add \pounds custom-cmd, drop setProperties in favor of setProperty-only
OlgaRedozubova Mar 18, 2026
606d5ec
Add 66 symbol mappings and fix rightleftarrows/leftrightarrows swap
OlgaRedozubova Mar 18, 2026
9a889e8
Add backprime, backsimeq, Join mappings; fix bowtie to use join
OlgaRedozubova Mar 18, 2026
d2661a9
Add \atop → mat(delim: #none, …) and update PR spec
OlgaRedozubova Mar 18, 2026
80c2b09
Fix merged mo nodes: split multi-char Unicode symbols individually
OlgaRedozubova Mar 18, 2026
7399a3a
Add batch 3 symbols, fix merged mo split, longLeftrightharpoons collapse
OlgaRedozubova Mar 19, 2026
6d9fdc9
Fix \tripledash: suppress phantom #hide() and replace dashes with hyph
OlgaRedozubova Mar 19, 2026
27c5213
Use limits(base)_symbol for under-arrows instead of attach(base, b: s…
OlgaRedozubova Mar 19, 2026
f7aa98d
Add \underparen support: accent map, shorthand, overparen→underparen …
OlgaRedozubova Mar 19, 2026
4ac2d83
Harden and refactor: code review fixes across serializer modules
OlgaRedozubova Mar 19, 2026
b425479
Fix \brace and \brack: use mat(delim: ...) instead of binom()
OlgaRedozubova Mar 19, 2026
2da8065
Harden Typst serializer: error handling, cleanup, dedup, and edge-cas…
OlgaRedozubova Mar 19, 2026
b7a2350
Add Typst format docs: README examples, changelog, type TexConvertToT…
OlgaRedozubova Mar 19, 2026
0b92a65
Fix HTML tag: rename <typstmath_inline> to <typstmath-inline>
OlgaRedozubova Mar 19, 2026
7d8b222
Add include_typst to examples, fix react-app build with path polyfill
OlgaRedozubova Mar 19, 2026
f9cedb7
Set version 2.0.39
OlgaRedozubova Mar 19, 2026
6e4e27a
Add MMD_TYPES semantic block metadata for non-HTML converters
OlgaRedozubova Mar 24, 2026
e0ff805
Add abstract_title to MMD_TYPES, tag abstract heading tokens
OlgaRedozubova Mar 24, 2026
2494cb6
Add figure and table to MMD_TYPES, tag table/figure tokens with metadata
OlgaRedozubova Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/node_modules
/examples
/tests
/pr-specs
webpack.config.js
webpack.dev.js
.gitignore
Expand Down
95 changes: 92 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,14 @@ class ConvertForm extends React.Component {

#### [Example of Latex to mathml/asciimath/tsv conversion](https://github.com/Mathpix/mathpix-markdown-it/tree/master/examples/react-app/use-parseMarkdownByHTML-method)

Rendering methods have the ability to convert `Latex` representation to such formats as: `mathml`, `asciimath`, `tsv`
Rendering methods have the ability to convert `Latex` representation to such formats as: `mathml`, `asciimath`, `typst`, `tsv`

```js
const options = {
outMath: { //You can set which formats should be included into html result
include_mathml: true,
include_asciimath: true,
include_typst: true,
include_latex: true,
include_svg: true, // sets in default
include_tsv: true,
Expand All @@ -291,6 +292,8 @@ For `Latex` formulas, the result will be:
<span class="math-inline">
<mathml style="display: none">...</mathml>
<asciimath style="display: none">...</asciimath>
<typstmath style="display: none">...</typstmath>
<typstmath-inline style="display: none">...</typstmath-inline>
<latex style="display: none">...</latex>
<mjx-comtainer class="MathJax" jax="SVG">..</mjx-comtainer>
</span>
Expand All @@ -317,6 +320,14 @@ For `Latex` formulas:
"type": "asciimath",
"value": "x^(x)"
},
{
"type": "typst",
"value": "x^x"
},
{
"type": "typst_inline",
"value": "x^x"
},
{
"type": "latex",
"value": "x^x"
Expand Down Expand Up @@ -420,6 +431,76 @@ const parsed = MathpixMarkdownModel.parseMarkdownByHTML(html, false);
```


### Latex to Typst math conversion

You can convert LaTeX math directly to [Typst](https://typst.app/) math notation:

```js
const { MathJax } = require('mathpix-markdown-it/lib/mathjax/index.js');

const result = MathJax.TexConvertToTypstData('\\frac{a}{b}');
// result = { typstmath: 'frac(a, b)', typstmath_inline: 'frac(a, b)' }
// result.error is undefined (set only when LaTeX is invalid)
```

Conversion examples — note how Typst uses function-call syntax instead of LaTeX backslash commands:

```js
// Fractions: \frac{}{} → frac()
MathJax.TexConvertToTypstData('\\frac{x+1}{x-1}');
// { typstmath: 'frac(x + 1, x - 1)', typstmath_inline: 'frac(x + 1, x - 1)' }

// Roots: \sqrt → sqrt(), \sqrt[n] → root()
MathJax.TexConvertToTypstData('\\sqrt[3]{x}');
// { typstmath: 'root(3, x)', typstmath_inline: 'root(3, x)' }

// Matrices: \begin{pmatrix} → mat(delim: "(", ...)
MathJax.TexConvertToTypstData('\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix}');
// { typstmath: 'mat(delim: "(", \n a, b;\n c, d,\n)',
// typstmath_inline: 'mat(delim: "(", \n a, b;\n c, d,\n)' }

// Cases: \begin{cases} → cases()
MathJax.TexConvertToTypstData('\\begin{cases} x & \\text{if } x > 0 \\\\ -x & \\text{otherwise} \\end{cases}');
// { typstmath: 'cases(\n x & "if " x > 0,\n - x & "otherwise",\n)',
// typstmath_inline: 'cases(\n x & "if " x > 0,\n - x & "otherwise",\n)' }

// Delimiters: \lfloor..\rfloor → floor(), \|..\| → norm()
MathJax.TexConvertToTypstData('\\lfloor x/2 \\rfloor + \\lceil y \\rceil');
// { typstmath: 'floor(x\\/ 2) + ceil(y)', typstmath_inline: 'floor(x\\/ 2) + ceil(y)' }

// Blackboard bold: \mathbb{Z} → ZZ
MathJax.TexConvertToTypstData('\\mathbb{Z}');
// { typstmath: 'ZZ', typstmath_inline: 'ZZ' }

// Custom operators: \operatorname*{} → op("", limits: #true)
MathJax.TexConvertToTypstData('\\operatorname*{argmax}_{x} f(x)');
// { typstmath: 'op("argmax", limits: #true)_x f(x)',
// typstmath_inline: 'op("argmax", limits: #true)_x f(x)' }

// Partial derivatives
MathJax.TexConvertToTypstData('\\frac{\\partial f}{\\partial x}');
// { typstmath: 'frac(partial f, partial x)', typstmath_inline: 'frac(partial f, partial x)' }

// Text in math: \text{} → ""
MathJax.TexConvertToTypstData('x \\text{ and } y');
// { typstmath: 'x " and " y', typstmath_inline: 'x " and " y' }
```

The converter returns two representations — `typstmath` (block, may include Typst code-mode wrappers like `#math.equation()`, `#box()`) and `typstmath_inline` (pure math-mode, safe for inline `$...$` contexts). They differ for `\boxed`, `\tag`, and similar constructs:

```js
// \boxed — block wraps in #box(), inline is plain math:
MathJax.TexConvertToTypstData('\\boxed{a+b}');
// { typstmath: '#align(center, box(stroke: 0.5pt, inset: 3pt, $a + b$))',
// typstmath_inline: 'a + b' }

// Invalid LaTeX — empty typst, error message returned:
MathJax.TexConvertToTypstData('\\frac{');
// { typstmath: '', typstmath_inline: '', error: 'Missing close brace' }
```

When included via `outMath` options (`include_typst: true`), Typst output is also available through the rendering pipeline alongside MathML, AsciiMath, and other formats.

### Example of the include_sub_math option usage for tables containing nested tables and formulas

#### parseMarkdownByHTML(html: string, include_sub_math: boolean = true)
Expand All @@ -431,6 +512,7 @@ const options = {
outMath: {
include_asciimath: true,
include_mathml: true,
include_typst: true,
include_latex: true,
include_svg: true,
include_tsv: true,
Expand All @@ -457,16 +539,22 @@ const parsed = MathpixMarkdownModel.parseMarkdownByHTML(html);

{ type: 'mathml', value: '<math xmlns="http://www.w3.org/1998/Math/MathML">\n <msup>\n <mi>x</mi>\n <mn>1</mn>\n </msup>\n</math>' },
{ type: 'asciimath', value: 'x^(1)' },
{ type: 'typst', value: 'x^1' },
{ type: 'typst_inline', value: 'x^1' },
{ type: 'latex', value: 'x^1' },
{ type: 'svg', value: '<svg >..</svg>' },

{ type: 'mathml', value: '<math xmlns="http://www.w3.org/1998/Math/MathML">\n <msup>\n <mi>y</mi>\n <mn>1</mn>\n </msup>\n</math>' },
{ type: 'asciimath', value: 'y^(1)' },
{ type: 'typst', value: 'y^1' },
{ type: 'typst_inline', value: 'y^1' },
{ type: 'latex', value: 'y^1' },
{ type: 'svg', value: '<svg ></svg>' },

{ type: 'mathml', value: '<math xmlns="http://www.w3.org/1998/Math/MathML">\n <msup>\n <mi>z</mi>\n <mn>1</mn>\n </msup>\n</math>' },
{ type: 'asciimath', value: 'z^(1)' },
{ type: 'typst', value: 'z^1' },
{ type: 'typst_inline', value: 'z^1' },
{ type: 'latex', value: 'z^1' },
{ type: 'svg', value: '<svg ></svg>' }
]
Expand All @@ -479,6 +567,7 @@ const options = {
outMath: {
include_asciimath: true,
include_mathml: true,
include_typst: true,
include_latex: true,
include_svg: true,
include_tsv: true,
Expand Down
19 changes: 19 additions & 0 deletions doc/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# March 2026

## [2.0.39] - Add Typst math format output

- New Feature:
- LaTeX-to-Typst math conversion via `MathJax.TexConvertToTypstData(latex)`. Returns `{ typstmath, typstmath_inline, error? }`.
- `typstmath` — block representation (may include `#math.equation()`, `#box()`, `#align()` wrappers).
- `typstmath_inline` — pure math-mode output, safe for inline `$...$` contexts.
- `include_typst: true` option in `outMath` to include `<typstmath>` and `<typstmath_inline>` tags in rendered HTML.

- Supported constructs:
- Fractions (`\frac` → `frac()`), roots (`\sqrt` → `sqrt()`, `\sqrt[n]` → `root()`), matrices (`pmatrix`/`bmatrix`/`vmatrix` → `mat()`), cases (`\begin{cases}` → `cases()`).
- Delimiter optimization: `\lfloor..\rfloor` → `floor()`, `\lceil..\rceil` → `ceil()`, `\|..\|` → `norm()`, `|..|` → `abs()`.
- Blackboard bold shorthands: `\mathbb{Z}` → `ZZ`, `\mathbb{N}` → `NN`, etc.
- Custom operators: `\operatorname*{argmax}` → `op("argmax", limits: #true)`.
- Text in math: `\text{...}` → `"..."`.
- Tags, labels, equation numbering, aligned environments, `\boxed`, accents, cancellations, colors, and 4200+ test cases.

- Error Handling:
- `merror` nodes (invalid LaTeX) return `{ typstmath: '', typstmath_inline: '', error: '...' }` — errors never appear in Typst output.

## [2.0.38] - Fix infinite loop in `inlineMmdIcon` and `inlineDiagbox` silent mode

- Bug Fix:
Expand Down
4 changes: 2 additions & 2 deletions es5/browser/auto-render.js

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions es5/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion es5/context-menu.js

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions es5/index.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const options = {
outMath: {
include_mathml: true,
include_asciimath: true,
include_typst: true,
include_latex: true,
include_svg: true,
include_tsv: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const options = {
outMath: {
include_mathml: true,
include_asciimath: true,
include_typst: true,
include_latex: true,
include_svg: true,
include_tsv: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ let options = {
outMath: {
include_asciimath: true,
include_mathml: true,
include_typst: true,
include_latex: true,
include_svg: true,
include_tsv: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const options = {
outMath: {
include_asciimath: true,
include_mathml: true,
include_typst: true,
include_latex: true,
include_svg: true,
include_tsv: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = function override(config) {
config.resolve.fallback = {
...config.resolve.fallback,
path: require.resolve('path-browserify'),
};
return config;
};
10 changes: 7 additions & 3 deletions examples/react-app/use-parseMarkdownByHTML-method/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
"react-dom": "^18.2.0",
"react-scripts": "^5.0.1"
},
"devDependencies": {
"path-browserify": "^1.0.1",
"react-app-rewired": "^2.2.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
"eslintConfig": {
Expand Down
10 changes: 10 additions & 0 deletions examples/react-app/use-parseMarkdownByHTML-method/src/form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class ConvertForm extends React.Component {
result: '',
include_mathml: true,
include_asciimath: true,
include_typst: true,
include_latex: true,
include_svg: true,
include_tsv: true,
Expand Down Expand Up @@ -54,6 +55,7 @@ class ConvertForm extends React.Component {
outMath: {
include_mathml: this.state.include_mathml,
include_asciimath: this.state.include_asciimath,
include_typst: this.state.include_typst,
include_latex: this.state.include_latex,
include_svg: this.state.include_svg,
include_tsv: this.state.include_tsv,
Expand Down Expand Up @@ -91,6 +93,14 @@ class ConvertForm extends React.Component {
onChange={this.handleInputChange} />
asciimath
</label>
<label>
<input
name="include_typst"
type="checkbox"
checked={this.state.include_typst}
onChange={this.handleInputChange} />
typst
</label>
<label>
<input
name="include_latex"
Expand Down
2 changes: 2 additions & 0 deletions lib/contex-menu/menu/consts.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export declare enum eMathType {
mathmlword = "mathmlword",
tsv = "tsv",
csv = "csv",
typst = "typst",
typst_inline = "typst_inline",
table_markdown = "table-markdown",
smiles = "smiles"
}
4 changes: 4 additions & 0 deletions lib/contex-menu/menu/consts.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/contex-menu/menu/consts.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions lib/contex-menu/menu/menu-item.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading