@webc.site/math 0.1.20 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +95 -783
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -4,882 +4,194 @@
4
4
 
5
5
  <a id="en"></a>
6
6
 
7
- # @webc.site/math
7
+ # @webc.site/math : The world's smallest and fastest web Markdown formula renderer
8
8
 
9
- ### The world's smallest and fastest web Markdown formula renderer
9
+ ## 1. Features
10
10
 
11
- <a href="https://www.npmjs.com/package/@webc.site/math" target="_blank"><img src="https://raw.githubusercontent.com/webc-site/math/dev/readme/en/svg/badge.npm.svg" alt="npm" /></a>
12
- &nbsp;&nbsp;
13
- <a href="https://github.com/webc-site/math" target="_blank"><img src="https://raw.githubusercontent.com/webc-site/math/dev/readme/en/svg/badge.github.svg" alt="github" /></a>
14
- &nbsp;&nbsp;
15
- <a href="https://math.webc.site" target="_blank"><img src="https://raw.githubusercontent.com/webc-site/math/dev/readme/en/svg/badge.demo.svg" alt="demo" /></a>
11
+ This project compiles LaTeX math formulas into browser-native MathML Core markup, achieving zero-overhead rendering without client-side layout engines.
16
12
 
17
- No need to load hundreds of KB of KaTeX/MathJax and large font packages. At just ~4KB (Gzipped), it compiles LaTeX equations into native MathML supported by modern browsers, achieving zero-overhead rendering.
13
+ - **High Performance**: Compiles TeX formulas directly to native MathML. Processing speed reaches ~329,000 operations per second, which is approximately 3.6 times faster than KaTeX and 48 times faster than MathJax.
14
+ - **Ultra-lightweight**: The package size is 7.78 KB (3.58 KB gzipped), minimizing initial page load times.
15
+ - **Zero Runtime Dependencies**: Renders math using the browser's native C++ layout engine instead of loading heavy client-side JavaScript formatting libraries.
16
+ - **Robust Fault Tolerance**: Automatically catches syntax errors like unclosed brackets, reverting to raw TeX string output to prevent application crashes.
17
+ - **Universal Compatibility**: Generates standard MathML tags suitable for Server-Side Rendering (SSR), Static Site Generation (SSG), and Client-Side Rendering (CSR).
18
18
 
19
- - [Core Advantages](#core-advantages)
20
- - [What is MathML?](#what-is-mathml)
21
- - [Why Compile TeX Formulas to MathML?](#why-compile-tex-formulas-to-mathml)
22
- - [Benchmark](#benchmark)
23
- - [1. Size Comparison (Gzipped)](#1-size-comparison-gzipped)
24
- - [2. Generation Speed (Ops/sec)](#2-generation-speed-opssec)
25
- - [Usage](#usage)
26
- - [JavaScript Examples](#javascript-examples)
27
- - [CSS and Math Font Configuration](#css-and-math-font-configuration)
28
- - [Markdown Parser Plugins](#markdown-parser-plugins)
29
- - [Features](#features)
30
- - [Supported Syntax List](#supported-syntax-list)
31
- - [Unsupported Syntax](#unsupported-syntax)
32
- - [Error Handling and Fault Tolerance](#error-handling-and-fault-tolerance)
33
- - [Internal Error Codes](#internal-error-codes)
34
- - [Design and Workflow](#design-and-workflow)
35
- - [Module Stages](#module-stages)
36
- - [Adding New Syntax](#adding-new-syntax)
37
- - [1. Constant Definitions](#1-constant-definitions)
38
- - [2. Lexer](#2-lexer)
39
- - [3. Parser](#3-parser)
40
- - [4. Codegen](#4-codegen)
41
- - [Tech Stack](#tech-stack)
42
- - [Directory Structure](#directory-structure)
43
- - [Historical Background](#historical-background)
19
+ ## 2. Usage
44
20
 
45
- ## Core Advantages
21
+ ### Compilation Examples
46
22
 
47
- ### What is MathML?
48
-
49
- MathML (Mathematical Markup Language) is an XML-based standard for describing math formulas on the Web.
50
- Since January 2023 (following Chrome 109's native support for MathML Core), all major browser engines (Blink, Gecko, WebKit) natively support MathML. Pages can render formulas without loading third-party JavaScript layout libraries.
51
-
52
- ### Why Compile TeX Formulas to MathML?
53
-
54
- While MathML renders natively, its XML-based syntax is too verbose for direct writing. TeX remains the standard for authoring formulas (e.g., `$e^{i\pi} + 1 = 0$`).
55
- Traditional solutions (like MathJax or KaTeX) require loading hundreds of KB of JS/CSS layout engines and consume CPU for DOM calculations.
56
- `@webc.site/math` compiles TeX to MathML, offering several advantages for Client-Side Rendering (CSR):
57
-
58
- - **Lightweight**: Only 7.78 KB raw size (3.58 KB gzipped), with zero footprint on initial page load times.
59
- - **Zero Runtime Dependencies**: Performs translation at compile time, delegating all rendering, positioning, and layout to the browser's native C++ engine. No JS layout engine runs on the client.
60
- - **Low CPU Overhead**: Designed for high-frequency rendering scenarios like WYSIWYG editors, running smoothly even on low-end mobile devices.
61
- - **SSR-Friendly**: Outputs standard HTML MathML tags, working identically for client-side dynamic rendering or static server-side building (SSR/SSG).
62
-
63
- ## Benchmark
64
-
65
- ### 1. Size Comparison (Gzipped)
66
-
67
- | Library | Raw Size | Gzip Size | Size Ratio |
68
- | :---------------------------------------------------------- | :-------: | :-------: | :--------: |
69
- | [@webc.site/math](https://github.com/webc-site/math) (Ours) | 7.78 KB | 3.58 KB | 1.0 ⭐️ |
70
- | [KaTeX](https://github.com/KaTeX/KaTeX) | 264.79 KB | 75.15 KB | 21.0 |
71
- | [MathJax](https://github.com/mathjax/MathJax) | 971.04 KB | 278.39 KB | 77.7 |
72
-
73
- ![](demo/size.svg)
74
-
75
- ### 2. Generation Speed (Ops/sec)
76
-
77
- Based on compiling standard test equations (measured using [sh/bench/pk.js](https://github.com/webc-site/math/blob/dev/sh/bench/pk.js)):
78
-
79
- - **@webc.site/math (Ours)**: ~329,000 ops/s (1.0 ⭐️)
80
- - **[KaTeX](https://github.com/KaTeX/KaTeX)**: ~92,000 ops/s (~3.6x slower)
81
- - **[MathJax](https://github.com/mathjax/MathJax)**: ~6,700 ops/s (~48.8x slower)
82
-
83
- ![](demo/speed.svg)
84
-
85
- ## Usage
86
-
87
- ### JavaScript Examples
88
-
89
- #### 1. Render TeX Formulas Directly
90
-
91
- Use `@webc.site/math` to compile TeX formulas directly into MathML (ideal for Markdown parser plugins):
23
+ #### Render TeX Formulas Directly
92
24
 
93
25
  ```javascript
94
26
  import mathml from "@webc.site/math";
95
27
 
96
- const tex = "e^{i\\pi} + 1 = 0";
97
- const html = mathml(tex, true); // true for block math, false/empty for inline math
28
+ const html = mathml("e^{i\\pi} + 1 = 0", true); // Second parameter sets block style
98
29
  ```
99
30
 
100
- #### 2. Replace Formulas in Markdown
101
-
102
- Use `@webc.site/math/md.js` to automatically detect and replace inline/block formulas in Markdown text with MathML (requires passing the math compiler):
31
+ #### Replace Formulas in Markdown Text
103
32
 
104
33
  ```javascript
105
34
  import mdMath from "@webc.site/math/md.js";
106
35
  import compile from "@webc.site/math";
107
36
 
108
- const markdown = "Euler's identity: $$e^{i\\pi} + 1 = 0$$";
109
- const html = mdMath(markdown, compile);
110
-
111
- console.log(html);
112
- // Output: Euler's identity: <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>e</mi><mrow><mi>i</mi><mi>π</mi></mrow></msup><mo>+</mo><mn>1</mn><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">e^{i\pi} + 1 = 0</annotation></semantics></math>
37
+ const html = mdMath("Euler's identity: $$e^{i\\pi} + 1 = 0$$", compile);
113
38
  ```
114
39
 
115
- ### CSS and Math Font Configuration
40
+ ### Font and CSS Configuration
116
41
 
117
- To ensure beautifully typeset browser-native math equations, using a math font is recommended. We recommend the **Latin Modern Math** font from the `18s` package (derived from Donald Knuth's classical Computer Modern, supporting OpenType MATH table features).
42
+ To ensure optimal layout and typesetting, configure math fonts. It is recommended to use the OpenType **Latin Modern Math** font from the `18s` package.
118
43
 
119
- #### 1. Online Reference (Recommended)
120
-
121
- Import the online font in CSS:
122
-
123
- ```css
124
- /* Import the bundle (includes Source Han Sans t, monospace c, and math font m) */
125
- @import url("//registry.npmmirror.com/18s/0.2.24/files/_.css");
126
- ```
127
-
128
- Or import the math font `m` only:
129
-
130
- ```css
131
- @import url("//registry.npmmirror.com/18s/0.2.24/files/m.css");
132
- ```
133
-
134
- #### 2. Configure CSS Style
135
-
136
- Set the font family for the `math` tag in your global CSS stylesheet.
137
-
138
- ##### Option A: Using Online Fonts (Recommended for best visual quality)
139
-
140
- For projects importing the `18s` font assets (which contains the optimized math font `m` and Source Han Sans `t`):
44
+ #### CSS Font Styling
141
45
 
142
46
  ```css
143
47
  math {
144
- /* m is the math font, t is Source Han Sans (optimized with font slicing for Chinese characters to boost loading performance), math is system math font, sans-serif is default fallback */
145
48
  font-family: m, t, math, sans-serif;
146
49
  }
147
50
  ```
148
51
 
149
- ##### Option B: Using System Math Fonts (Zero external font assets)
150
-
151
- Leverage system-default math fonts to minimize loading size:
152
-
153
- ```css
154
- math {
155
- /* Prioritize system-built-in math fonts, fallback to standard CSS 'math' generic font family, and use sans-serif as a final fallback */
156
- font-family: "STIX Two Math", "Latin Modern Math", "Cambria Math", math, sans-serif;
157
- }
158
- ```
159
-
160
- #### 3. Integration with Build Tools
161
-
162
- Install the `18s` font package via npm, and import it at your project entry using build tools like Vite or Webpack:
163
-
164
- ```bash
165
- npm install 18s
166
- ```
167
-
168
- Import font styles in your project entry file:
169
-
170
- ##### Method A: Import in JS/TS Entry File
171
-
172
- ```javascript
173
- // Import the bundle (includes Source Han Sans t, monospace c, and math font m)
174
- import "18s/_.css";
175
- ```
176
-
177
- Or import math font `m` only:
178
-
179
- ```javascript
180
- import "18s/m.css";
181
- ```
182
-
183
- ##### Method B: Import in CSS Stylesheet
184
-
185
- ```css
186
- /* Import the bundle (includes Source Han Sans t, monospace c, and math font m) */
187
- @import "18s/_.css";
188
- ```
189
-
190
- Or import math font `m` only:
191
-
192
- ```css
193
- @import "18s/m.css";
194
- ```
195
-
196
- ### Markdown Parser Plugins
197
-
198
- #### 1. Marked Extension
199
-
200
- Use [`@webc.site/math-marked`](https://www.npmjs.com/package/@webc.site/math-marked) to automatically parse and compile inline (`$...$`) and block (`$$...$$`) formulas:
201
-
202
- ```javascript
203
- import { marked } from "marked";
204
- import mathMarked from "@webc.site/math-marked";
205
-
206
- marked.use(mathMarked());
207
-
208
- const html = marked.parse("Euler's identity: $$e^{i\\pi} + 1 = 0$$");
209
- ```
210
-
211
- #### 2. Remark Plugin
212
-
213
- Use [`@webc.site/math-remark`](https://www.npmjs.com/package/@webc.site/math-remark) to replace `inlineMath` and `math` nodes in Unified/Remark AST with native MathML HTML:
214
-
215
- ```javascript
216
- import { unified } from "unified";
217
- import remarkParse from "remark-parse";
218
- import remarkMath from "remark-math";
219
- import mathRemark from "@webc.site/math-remark";
220
- import remarkHtml from "remark-html";
221
-
222
- const html = await unified()
223
- .use(remarkParse)
224
- .use(remarkMath)
225
- .use(mathRemark)
226
- .use(remarkHtml, { sanitize: false })
227
- .process("Euler's identity: $$e^{i\\pi} + 1 = 0$$");
228
- ```
229
-
230
- #### 3. Markdown-it Plugin
52
+ ## 3. Design
231
53
 
232
- Use [`@webc.site/math-markdown-it`](https://www.npmjs.com/package/@webc.site/math-markdown-it) to automatically parse and render inline/block formulas in `markdown-it`:
233
-
234
- ```javascript
235
- import markdownit from "markdown-it";
236
- import mathMarkdownIt from "@webc.site/math-markdown-it";
237
-
238
- const md = markdownit().use(mathMarkdownIt);
239
-
240
- const html = md.render("Euler's identity: $$e^{i\\pi} + 1 = 0$$");
241
- ```
242
-
243
- ## Features
244
-
245
- - **Highly Complete**: Successfully compiles thousands of math formulas from KaTeX/MathJax official test suites (see [extract](https://github.com/webc-site/math/tree/dev/extract)).
246
- - **Fault-Tolerant**: Syntax errors (such as unclosed `\left`/`\right`) are automatically caught and degraded to display raw TeX code, preventing application crashes.
247
- - **Fast Compiler**: Compiles TeX formulas directly to semantic MathML without external parser dependencies.
248
- - **Markdown Integration**: Automatically parses inline math (`$formula$`) and block math (`$$formula$$`) in Markdown text.
249
- - **Standard Compatibility**: Outputs valid MathML elements supported natively by modern browsers.
250
-
251
- ## Supported Syntax List
252
-
253
- Designed to be extremely lightweight, this library supports the most commonly used math typesetting syntaxes:
254
-
255
- - **Basic Arithmetic & Symbols**: Numbers, English letters, basic operators (`+`, `-`, `*`, `/`, `=`, `<`, `>`, `(`, `)`, `[`, `]`, `.`). Note that `-` automatically maps to minus sign `\u2212`, `*` maps to asterisk `\u2217`, and `/` is rendered in upright normal font.
256
- - **Subscripts & Superscripts**:
257
- - Superscript `^` (e.g., `x^2`)
258
- - Subscript `_` (e.g., `x_i`)
259
- - Both subscripts and superscripts (e.g., `x_i^2` or `x^2_i`)
260
- - Subscripts/superscripts of sum, integration, and other big operators will automatically appear in limits form (e.g., `\sum_{i=1}^n`, `\int_a^b`)
261
- - **Fractions**: `\frac{numerator}{denominator}` (e.g., `\frac{a}{b}`)
262
- - **Roots**: Square root `\sqrt{x}` and $n$-th root `\sqrt[n]{x}`
263
- - **Overlines & Bars**: `\overline{x}` and the shorthand `\bar{x}`
264
- - **Delimiters & Brackets**: `\left` and `\right` structures (e.g., `\left( ... \right)`). Supported delimiters include:
265
- - Parentheses: `(` and `)`
266
- - Square brackets: `[` and `]`
267
- - Braces: `\{` and `\}`
268
- - Angle brackets: `<` and `>`
269
- - Vertical bars: `|` or `\|`
270
- - Empty delimiter: `.` (hides the delimiter on that side, e.g., `\left. \frac{df}{dx} \right| _0`)
271
- - **Text Mode**: `\text{...}` (e.g., `\text{if }`), extracts literal text inside braces and renders it as MathML `<mtext>` in normal upright font.
272
- - **Horizontal Spacing**: Supports `\quad` (1em spacing) and `\qquad` (2em spacing).
273
- - **Styles, Strikethroughs & Phantom**:
274
- - Border: `\boxed{...}` (adds a border around the formula, e.g., `\boxed{x+y}`)
275
- - Strikethroughs: `\cancel{...}` (strikes through with a slash, e.g., `\cancel{x}`) and `\sout{...}` (strikes through with a horizontal line, e.g., `\sout{y}`)
276
- - Hiding & Spacing: `\phantom{...}` (creates an invisible space with the same width and height, e.g., `\phantom{x}`)
277
- - **Common Functions**: `\sin`, `\cos`, `\tan`, `\cot`, `\sec`, `\csc`, `\log`, `\lg`, `\ln`, `\lim`, `\exp`, `\max`, `\min`, `\sup`, `\inf`, `\det`, `\gcd`, `\arcsin`, `\arccos`, `\arctan`, `\sinh`, `\cosh`, `\tanh`, `\coth`, `\deg`, `\arg`. Subscripts/superscripts of limit-like operators (`\lim`, `\max`, `\min`, `\sup`, `\inf`) appear as limits (directly below/above) in display mode.
278
- - **Modulo**: `\pmod{...}` (adds a modulo parenthesis, e.g., `\pmod{m}` renders as $(mod\ m)$)
279
- - **Greek Letters**:
280
- - Lowercase: `\alpha` ($\alpha$), `\beta` ($\beta$), `\gamma` ($\gamma$), `\delta` ($\delta$), `\epsilon` ($\epsilon$), `\zeta` ($\zeta$), `\eta` ($\eta$), `\theta` ($\theta$), `\iota` ($\iota$), `\kappa` ($\kappa$), `\lambda` ($\lambda$), `\mu` ($\mu$), `\nu` ($\nu$), `\xi` ($\xi$), `\pi` ($\pi$), `\rho` ($\rho$), `\sigma` ($\sigma$), `\tau` ($\tau$), `\upsilon` ($\upsilon$), `\phi` ($\phi$), `\chi` ($\chi$), `\psi` ($\psi$), `\omega` ($\omega$)
281
- - Uppercase (rendered in normal upright font): `\Delta` ($\Delta$), `\Gamma` ($\Gamma$), `\Theta` ($\Theta$), `\Lambda` ($\Lambda$), `\Xi` ($\Xi$), `\Pi` ($\Pi$), `\Sigma` ($\Sigma$), `\Upsilon` ($\Upsilon$), `\Phi` ($\Phi$), `\Psi` ($\Psi$), `\Omega` ($\Omega$)
282
- - **Operators & Relations**:
283
- - `\le` / `\leq` ($\le$), `\ge` / `\geq` ($\ge$), `\ne` / `\neq` ($\ne$)
284
- - `\cdot` ($\cdot$), `\times` ($\times$), `\pm` ($\pm$), `\mp` ($\mp$), `\div` ($\div$), `\infty` ($\infty$)
285
- - `\approx` ($\approx$), `\sim` ($\sim$), `\cong` ($\cong$), `\propto` ($\propto$), `\equiv` ($\equiv$), `\perp` ($\perp$), `\parallel` ($\parallel$)
286
- - **Calculus, Sets & Logic**:
287
- - Gradient: `\nabla` ($\nabla$), partial differential: `\partial` ($\partial$)
288
- - Quantifiers & operations: `\forall` ($\forall$), `\exists` ($\exists$), `\neg` ($\neg$), `\land` ($\land$), `\lor` ($\lor$)
289
- - Set relations: `\in` ($\in$), `\notin` ($\notin$), `\ni` ($\ni$), `\subset` ($\subset$), `\supset` ($\supset$), `\subseteq` ($\subseteq$), `\supseteq` ($\supseteq$)
290
- - Set operations: `\cup` ($\cup$), `\cap` ($\cap$), empty set: `\emptyset` ($\emptyset$)
291
- - Special variables & constants: `\ell` ($\ell$), `\hbar` ($\hbar$)
292
- - Large operators: summation `\sum` ($\sum$), integration `\int` ($\int$)
293
- - **Arrows**:
294
- - Single arrows: `\to` / `\rightarrow` ($\rightarrow$), `\leftarrow` / `\gets` ($\leftarrow$), `\Leftarrow` ($\Leftarrow$), `\Rightarrow` ($\Rightarrow$)
295
- - Double arrows: `\leftrightarrow` ($\leftrightarrow$), `\Leftrightarrow` ($\Leftrightarrow$)
296
- - **Dots**:
297
- - Baseline dots: `\dots` / `\ldots` ($\dots$)
298
- - Centered dots: `\cdots` ($\cdots$)
299
- - **Matrices & Multi-line Layouts**:
300
- - Matrix environments: `matrix`, `pmatrix`, `bmatrix`, `vmatrix`, `Vmatrix` (e.g., `\begin{pmatrix} a & b \\ c & d \end{pmatrix}`)
301
- - Cases: `cases` (e.g., `\begin{cases} x & x \ge 0 \\ -x & x < 0 \end{cases}`)
302
- - General arrays: `array`
303
- - Line breaks & alignment: Use `\\`, `\\*` or `\\[width]` (e.g., `\\[10px]`) for line breaks, and `&` for column alignment.
304
-
305
- ## Unsupported Syntax
306
-
307
- Currently, the following LaTeX extensions, macros, or fine-tuning style directives are not supported:
308
-
309
- 1. **Macro Definitions**: `\newcommand`, `\renewcommand`, `\providecommand`, `\gdef`, `\let`, etc.
310
- 2. **Background Colors & Advanced Borders**: `\colorbox`, `\fcolorbox`, `\cellcolor`, etc. (while `\boxed` is supported).
311
- 3. **Other Strikethroughs**: `\bcancel`, `\xcancel`, etc. (while `\cancel` and `\sout` are supported).
312
- 4. **Advanced Layouts & Spacing**: `\hphantom`, `\vphantom`, `\smash`, etc. (while `\phantom` is supported).
313
- 5. **Chemistry Package Extensions**: Chemical formula macros like `\ce{...}` (mhchem).
314
- 6. **Verbatim Text**: `\verb`, etc.
315
- 7. **Advanced Positionings**: `\sideset`, `\prescript`, `\cramped`, `\flatfrac`, etc.
316
- 8. **Equation Numbering & Custom Tags**: `\tag`, `\newtagform`, `\usetagform`, etc.
317
- 9. **Arbitrary Operator Limits**: Except for predefined big operators (`\sum`, `\int`) and limit-like operators (`\lim`), using `\limits` on arbitrary commands or structures is not supported.
318
-
319
- ## Error Handling and Fault Tolerance
320
-
321
- When parsing Markdown text using `@webc.site/math/md.js`, syntax errors (such as an unclosed `\left`) are automatically caught and degraded to display raw TeX code (e.g., `$$x + \left( y$$`), without throwing JS exceptions. Therefore, you **do not** need to wrap it in a `try...catch` block.
322
-
323
- If you call the compiler core `@webc.site/math` directly with invalid LaTeX, it will throw an array containing an error code (see table below). Wrap direct core compiler calls in a `try...catch` block.
324
-
325
- ### Internal Error Codes
326
-
327
- | Error Code | Constant | Description | Trigger Example |
328
- | :--------: | :------------------ | :-------------------------------------------- | :------------------------------------ |
329
- | `0` | `ERR_EXTRA_END` | Extra or invalid `\end` command | `\end{matrix}` (no matching `\begin`) |
330
- | `1` | `ERR_MISSING_RIGHT` | `\left` delimiter missing a matching `\right` | `\left( x` |
331
- | `2` | `ERR_EXTRA_RIGHT` | Extra or invalid `\right` command | `x \right)` (no matching `\left`) |
332
- | `3` | `ERR_MISSING_BRACE` | Command missing required curly brace `{}` | `\text x` (missing braces) |
333
-
334
- ## Design and Workflow
335
-
336
- The parser processes the input Markdown string, isolates TeX expressions, and translates them into MathML structures.
54
+ The compiler extracts TeX math formulas from input Markdown text, runs lexical and syntax analyses, and generates semantic MathML markup.
337
55
 
338
56
  ```mermaid
339
57
  graph TD
340
- A[Input Markdown] --> B{Scanner}
341
- B -- Plain Text --> C[Output Buffer]
342
- B -- TeX Formula --> D[Lexer: Generate Tokens]
343
- D --> E[Parser: Generate AST]
344
- E --> F[Codegen: Generate MathML Tags]
345
- F --> G[Semantic Wrapper]
346
- G --> H[MathML Output]
347
- C --> I[Final HTML]
348
- H --> I
58
+ Input[Input Markdown] --> Scanner[Scanner: Locates Delimiters]
59
+ Scanner -- Plain Text --> Buffer[Output Buffer]
60
+ Scanner -- TeX Formula --> Lexer[Lexer: Tokenizes Input]
61
+ Lexer --> Parser[Parser: Builds AST]
62
+ Parser --> Codegen[Codegen: Translates to MathML Tags]
63
+ Codegen --> Wrapper[Semantic Wrapper]
64
+ Wrapper --> MathML[MathML Output]
65
+ Buffer --> Output[Final HTML]
66
+ MathML --> Output
349
67
  ```
350
68
 
351
- ### Module Stages
352
-
353
- 1. **Scanner**: Scans the input string to locate formula delimiters (`$` and `$$`).
354
- 2. **Lexer**: Breaks the TeX string into tokens like numbers, variables, operators, and control commands.
355
- 3. **Parser**: Translates tokens into an Abstract Syntax Tree (AST), supporting operations like subscripts, superscripts, fractions, and built-in functions.
356
- 4. **Codegen**: Maps AST nodes to standard MathML Core elements (`<mi>`, `<mo>`, `<mn>`, `<mfrac>`, `<msup>`, `<msub>`, `<msubsup>`).
357
-
358
- ## Adding New Syntax
359
-
360
- To add support for a new LaTeX command, modify the following four core parts in order:
361
-
362
- ### 1. Constant Definitions
363
-
364
- Define the corresponding lexical token, AST node type, function name, or symbol mapping:
365
-
366
- - **Token Type**: Defined in [const/TOK.js](https://github.com/webc-site/math/blob/dev/src/const/TOK.js) (e.g., `export const TOK_MY_CMD = ...`).
367
- - **AST Node Type**: Defined in [const/TYPE.js](https://github.com/webc-site/math/blob/dev/src/const/TYPE.js) (e.g., `export const TYPE_MY_NODE = ...`).
368
- - **Environment Delimiters**: If adding a new environment (like a matrix or bracket type), configure its left and right delimiters in `ENV_DELIMS` inside [mathml.js](https://github.com/webc-site/math/blob/dev/src/mathml.js).
369
- - **Symbol Mapping**: For simple operators or symbols, map the command name to its Unicode character in `SYM_MAP` inside [const/SYM.js](https://github.com/webc-site/math/blob/dev/src/const/SYM.js).
370
- - **Math Function Name**: Define in `FUNC_NAMES` inside [const/FUNC.js](https://github.com/webc-site/math/blob/dev/src/const/FUNC.js).
371
-
372
- ### 2. Lexer
373
-
374
- The `lex(str)` function cuts the input LaTeX string into a token array. It is located in [lex.js](https://github.com/webc-site/math/blob/dev/src/lex.js).
375
-
376
- - If introducing a new special character or structure, update character matching logic in `lex` to push the correct `TOK_*` type and its literal value to the `tokens` array.
377
-
378
- ### 3. Parser
379
-
380
- The `parse(tokens, state)` function translates tokens into AST nodes. It is located in [parse.js](https://github.com/webc-site/math/blob/dev/src/parse.js).
381
-
382
- - **Command Parsing**: Handled primarily inside the `TOK_MAP[TOK_CMD]` function. When matching your command (e.g., `\mycmd`), read its arguments (using `read(tokens, state_ref)` or `grab(tokens, state_ref)`) and return the node array: `[TYPE_MY_NODE, arg1, arg2]`.
383
-
384
- ### 4. Codegen
385
-
386
- The `SHOW_MAP` dictionary maps AST nodes to standard MathML markup strings. It is located in [mathml.js](https://github.com/webc-site/math/blob/dev/src/mathml.js).
387
-
388
- - Register a renderer function: `[TYPE_MY_NODE]: ([_, arg1, arg2]) => nest("mylabel", arg1, arg2)`, formatting node data into native MathML tags.
389
-
390
- ## Tech Stack
69
+ ## 4. Tech Stack
391
70
 
392
71
  - **Runtime**: Bun, Node.js
393
- - **Build & Bundle**: SWC (compression), Vite (demo site)
394
- - **Quality Assurance**: oxlint, oxfmt
72
+ - **Linter & Formatter**: oxlint, oxfmt
73
+ - **Build Tool**: Vite, Rolldown, Lightning CSS
395
74
 
396
- ## Directory Structure
75
+ ## 5. Code Structure
397
76
 
398
77
  ```
399
78
  .
400
- ├── demo/ # Interactive demo site
401
- ├── const/ # Constants (preset formulas, language list)
402
- ├── i18n/ # Language translation configurations
403
- │ ├── index.js # Interactive demo logic
404
- ├── index.pug # Pug HTML template
405
- │ └── style.styl # Styling and themes
406
- ├── extract/ # Test case extraction scripts (from KaTeX / MathJax)
407
- ├── lib/ # Compilation output directory
408
- │ ├── package.json # Lightweight package.json for npm publication
409
- │ ├── README.md # Automatically generated distribution README
410
- │ ├── mathml.js # Minified core TeX compiler
411
- │ ├── mathml.js.map # Source map for mathml.js
412
- │ ├── md.js # Minified Markdown parser
413
- │ └── md.js.map # Source map for md.js
79
+ ├── demo/ # Interactive demo page
80
+ ├── extract/ # Test cases extraction scripts
81
+ ├── lib/ # Compiled distribution files
82
+ │ ├── mathml.js # Core compiler (minified)
83
+ └── md.js # Markdown math formula parser (minified)
414
84
  ├── src/ # Source code
415
- │ ├── const/ # Tokens, AST types, symbol maps, and function names
85
+ │ ├── const/ # Tokens, AST types, symbols and functions constants
416
86
  │ ├── lex.js # LaTeX lexer
417
- │ ├── parse.js # LaTeX parser (AST generation)
418
- │ ├── mathml.js # TeX-to-MathML compiler
419
- │ └── md.js # Markdown mathematical formula parser
87
+ │ ├── parse.js # LaTeX parser (AST builder)
88
+ │ ├── mathml.js # Core TeX-to-MathML compiler
89
+ │ └── md.js # Markdown parser entry
420
90
  ├── sh/ # Scripts
421
- ├── bench/ # Benchmark scripts, charts, and historical baseline data
422
- │ │ ├── pk.js # Size and speed comparison & chart generation
423
- │ │ ├── self.js # Regression tests
424
- │ │ ├── util.js # Shared helpers
425
- │ │ ├── chart.js # SVG chart component
426
- │ │ └── history.yml # YAML historical benchmark records
427
- │ └── check.js # Language file validation script
428
- ├── dev.js # Dev server script (Vite)
429
- ├── dist.js # Publish helper script (versioning and template rendering)
430
- ├── minify.js # Bundle compression script
431
- ├── package.json # Project package config
432
- ├── README.md # Automatically generated root README
433
- ├── README.mdt # Compilation template for README
434
- └── test.sh # Formatting, linting, and unit test execution script
91
+ └── bench/ # Benchmark suites and historical data
92
+ └── test.sh # Quality verification and test runner
435
93
  ```
436
94
 
437
- ## Historical Background
438
-
439
- Web-based math formula rendering traditionally relied on MathJax or KaTeX. These libraries load large JS/CSS packages and perform complex client-side calculations, causing noticeable loading delay or page layout shifting.
95
+ ## 6. Historical Background
440
96
 
441
- MathML Core specifies native mathematical typesetting in the browser. In 2023, the Blink engine introduced native support, enabling Chrome, Safari, and Firefox to natively lay out equations.
97
+ In the early history of the World Wide Web Consortium (W3C), MathML was proposed as a standard for mathematical notation in HTML5. However, implementation complexity caused fragmented support across browser engines. Chromium removed its initial MathML code in 2013 due to architectural and security issues, forcing web applications to load large, heavy layout libraries such as MathJax or KaTeX to calculate page styles and position symbols.
442
98
 
443
- In combination with the **Latin Modern Math** font (`m` from `18s` package), `@webc.site/math` acts as a translator, compiling TeX equations directly to native MathML Core tags to eliminate client-side rendering engines.
99
+ A decade later, in January 2023, Chrome 109 reintroduced native support for the MathML Core specification, which defines a subset of MathML optimized for browser performance. With WebKit (Safari), Gecko (Firefox), and Blink (Chrome/Edge) all supporting MathML Core natively, pages no longer require client-side JavaScript layout calculations. This project was created to compile LaTeX directly into native MathML tags at compile time, eliminating layout engine dependencies.
444
100
 
445
101
  ---
446
102
 
447
103
  <a id="zh"></a>
448
104
 
449
- # @webc.site/math
105
+ # @webc.site/math : 全球最小最快的网页Markdown公式渲染器
450
106
 
451
- ### 全球最小最快的网页 Markdown 公式渲染器
107
+ ## 1. 功能介绍
452
108
 
453
- <a href="https://www.npmjs.com/package/@webc.site/math" target="_blank"><img src="https://raw.githubusercontent.com/webc-site/math/dev/readme/zh/svg/badge.npm.svg" alt="npm" /></a>
454
- &nbsp;&nbsp;
455
- <a href="https://github.com/webc-site/math" target="_blank"><img src="https://raw.githubusercontent.com/webc-site/math/dev/readme/zh/svg/badge.github.svg" alt="github" /></a>
456
- &nbsp;&nbsp;
457
- <a href="https://math.webc.site" target="_blank"><img src="https://raw.githubusercontent.com/webc-site/math/dev/readme/zh/svg/badge.demo.svg" alt="demo" /></a>
109
+ 项目将 LaTeX 数学公式编译为浏览器原生支持的 MathML Core 标记,无需前端排版引擎,实现零运行开销渲染。
458
110
 
459
- 无需引入数百 KB KaTeX/MathJax 及庞大的字体包。包体积仅约 4KB (Gzip),将 LaTeX 公式编译为浏览器原生支持的 MathML,实现零运行开销的公式渲染。
111
+ - **高性能**:直接将 TeX 公式翻译为原生 MathML 标签。处理速度达每秒 329,000 次操作,较 KaTeX 3.6 倍,较 MathJax 48 倍。
112
+ - **体积小**:包体积仅 7.78 KB(Gzip 压缩后 3.58 KB),不影响页面首次加载性能。
113
+ - **无运行时依赖**:利用浏览器底层的 C++ 原生引擎进行布局和渲染,免去加载前端 JavaScript 排版库的步骤。
114
+ - **健壮容错**:自动捕获未闭合括号等语法错误,降级输出原始 TeX 字符串,防止页面程序崩溃。
115
+ - **通用兼容**:输出标准的 MathML 元素,适用于服务端渲染(SSR)、静态站点生成(SSG)和前端动态转换。
460
116
 
461
- - [核心优势](#核心优势)
462
- - [什么是 MathML?](#什么是-mathml)
463
- - [为什么将 TeX 编译为 MathML?](#为什么将-tex-编译为-mathml)
464
- - [性能对比](#性能对比)
465
- - [1. 体积对比(Gzip 压缩)](#1-体积对比gzip-压缩)
466
- - [2. 生成速度对比](#2-生成速度对比)
467
- - [使用方法](#使用方法)
468
- - [JavaScript 示例](#javascript-示例)
469
- - [CSS 与数学字体配置](#css-与数学字体配置)
470
- - [Markdown 解析器插件](#markdown-解析器插件)
471
- - [功能特性](#功能特性)
472
- - [支持的语法清单](#支持的语法清单)
473
- - [不支持的语法](#不支持的语法)
474
- - [错误处理与容错机制](#错误处理与容错机制)
475
- - [内部错误码](#内部错误码)
476
- - [设计思路与调用流程](#设计思路与调用流程)
477
- - [模块运行流程](#模块运行流程)
478
- - [如何添加新语法](#如何添加新语法)
479
- - [1. 常量定义](#1-常量定义)
480
- - [2. 词法分析](#2-词法分析)
481
- - [3. 语法分析](#3-语法分析)
482
- - [4. 代码渲染](#4-代码渲染)
483
- - [技术堆栈](#技术堆栈)
484
- - [目录结构](#目录结构)
485
- - [历史背景](#历史背景)
117
+ ## 2. 使用演示
486
118
 
487
- ## 核心优势
119
+ ### 编译示例
488
120
 
489
- ### 什么是 MathML?
490
-
491
- MathML(数学标记语言)是描述数学公式结构与语义的 XML 标准。
492
- 自 Chrome 109(2023年1月)起,所有主流浏览器引擎(Blink、Gecko、WebKit)均已原生支持 MathML Core 标准。页面无需加载 JS 排版库即可渲染公式。
493
-
494
- ### 为什么将 TeX 编译为 MathML?
495
-
496
- MathML 语法基于 XML,不便于直接书写。日常书写的公式标准是 TeX 语法(如 `$e^{i\pi} + 1 = 0$`)。
497
- 传统库(如 MathJax、KaTeX)在前端运行时,需加载数百 KB 的排版引擎,并消耗 CPU 进行布局计算。
498
- 本库将 TeX 直接编译为 MathML,具有以下优势:
499
-
500
- - **体积小**:包体积仅 7.78 KB(Gzip 3.58 KB),不影响首屏加载。
501
- - **零运行依赖**:仅在编译期进行标签翻译,排版布局完全由浏览器底层的 C++ 原生引擎渲染,无需前端 JS 引擎介入,释放主线程。
502
- - **低 CPU 开销**:适用于编辑器实时预览等高频渲染场景,低配移动设备运行流畅。
503
- - **前后端通用**:输出标准 MathML 元素,适用于前端动态转换与服务端静态编译(SSR/SSG)。
504
-
505
- ## 性能对比
506
-
507
- ### 1. 体积对比(Gzip 压缩)
508
-
509
- | 库 | 原始体积 | Gzip 体积 | 尺寸比 |
510
- | :---------------------------------------------------------- | :-------: | :-------: | :----: |
511
- | [@webc.site/math](https://github.com/webc-site/math) (本库) | 7.78 KB | 3.58 KB | 1.0 ⭐️ |
512
- | [KaTeX](https://github.com/KaTeX/KaTeX) | 264.79 KB | 75.15 KB | 21.0 |
513
- | [MathJax](https://github.com/mathjax/MathJax) | 971.04 KB | 278.39 KB | 77.7 |
514
-
515
- ![](demo/size.svg)
516
-
517
- ### 2. 生成速度对比
518
-
519
- 基于经典公式循环编译测试(使用 [sh/bench/pk.js](https://github.com/webc-site/math/blob/dev/sh/bench/pk.js) 测得):
520
-
521
- - **@webc.site/math (本库)**: ~329,000 ops/s (1.0 ⭐️)
522
- - **[KaTeX](https://github.com/KaTeX/KaTeX)**: ~92,000 ops/s (约 3.6x 较慢)
523
- - **[MathJax](https://github.com/mathjax/MathJax)**: ~6,700 ops/s (约 48.8x 较慢)
524
-
525
- ![](demo/speed.svg)
526
-
527
- ## 使用方法
528
-
529
- ### JavaScript 示例
530
-
531
- #### 1. 直接渲染 TeX 公式
532
-
533
- 使用 `@webc.site/math` 将 TeX 公式编译为 MathML(适用于 Markdown 渲染插件开发):
121
+ #### 直接渲染 TeX 公式
534
122
 
535
123
  ```javascript
536
124
  import mathml from "@webc.site/math";
537
125
 
538
- const tex = "e^{i\\pi} + 1 = 0";
539
- const html = mathml(tex, true); // 第二个参数传 true 表示块级公式,传 false 或不传表示行内公式
126
+ const html = mathml("e^{i\\pi} + 1 = 0", true); // 第二参数设为 true 表示块级公式
540
127
  ```
541
128
 
542
- #### 2. 替换 Markdown 中的公式
543
-
544
- 使用 `@webc.site/math/md.js` 自动识别 Markdown 文本中的行内/块级公式并替换为 MathML(需传入公式编译器):
129
+ #### 替换 Markdown 文本中的公式
545
130
 
546
131
  ```javascript
547
132
  import mdMath from "@webc.site/math/md.js";
548
133
  import compile from "@webc.site/math";
549
134
 
550
- const markdown = "欧拉恒等式:$$e^{i\\pi} + 1 = 0$$";
551
- const html = mdMath(markdown, compile);
552
-
553
- console.log(html);
554
- // 输出: 欧拉恒等式:<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mi>e</mi><mrow><mi>i</mi><mi>π</mi></mrow></msup><mo>+</mo><mn>1</mn><mo>=</mo><mn>0</mn></mrow><annotation encoding="application/x-tex">e^{i\pi} + 1 = 0</annotation></semantics></math>
135
+ const html = mdMath("欧拉恒等式:$$e^{i\\pi} + 1 = 0$$", compile);
555
136
  ```
556
137
 
557
- ### CSS 与数学字体配置
138
+ ### 字体与 CSS 配置
558
139
 
559
- 为保证浏览器原生数学公式的排版,建议配置数学字体。推荐使用 `18s` 字体包的 **Latin Modern Math**(源自高德纳的 Computer Modern 字体,支持 OpenType 数学排版特性)。
140
+ 配置数学字体可确保排版美观。推荐使用 `18s` 字体包中的 **Latin Modern Math** 字体。
560
141
 
561
- #### 1. 在线引用
562
-
563
- 在 CSS 中通过 `@import` 引入在线字体:
564
-
565
- ```css
566
- /* 引入合并后的字体 CSS(包含思源黑体 t、代码字体 c 及数学字体 m) */
567
- @import url("//registry.npmmirror.com/18s/0.2.24/files/_.css");
568
- ```
569
-
570
- 或者仅按需引入数学字体 `m`:
571
-
572
- ```css
573
- @import url("//registry.npmmirror.com/18s/0.2.24/files/m.css");
574
- ```
575
-
576
- #### 2. 配置 CSS 样式
577
-
578
- 在全局 CSS 样式表中,为 `math` 标签指定数学字体。
579
-
580
- ##### 方案 A:使用在线字体(推荐,效果最完美)
581
-
582
- 引入 `18s` 提供的字体包(含切片优化思源黑体 `t` 与数学字体 `m`):
142
+ #### CSS 字体样式设置
583
143
 
584
144
  ```css
585
145
  math {
586
- /* m 为数学字体,t 为思源黑体(对中文字符进行了切片优化以提升加载性能),math 为系统数学字体,sans-serif 为系统默认无衬线字体 */
587
146
  font-family: m, t, math, sans-serif;
588
147
  }
589
148
  ```
590
149
 
591
- ##### 方案 B:使用系统数学字体(免引入外部资源)
592
-
593
- 直接使用系统内置数学字体以最小化体积:
594
-
595
- ```css
596
- math {
597
- /* 优先使用各平台内置的数学字体,其次降级到 CSS 标准 math 泛型,最后以系统默认无衬线字体作为后备 */
598
- font-family: "STIX Two Math", "Latin Modern Math", "Cambria Math", math, sans-serif;
599
- }
600
- ```
601
-
602
- #### 3. 配合构建工具引用
603
-
604
- 通过 npm 安装 `18s` 字体包,配合 Vite、Webpack 等构建工具在项目入口中引入:
605
-
606
- ```bash
607
- npm install 18s
608
- ```
609
-
610
- 在项目入口文件中引入字体样式:
611
-
612
- ##### 方式 A:在 JS/TS 入口中引入
613
-
614
- ```javascript
615
- // 引入合并后的字体 CSS(包含思源黑体 t、代码字体 c 及数学字体 m)
616
- import "18s/_.css";
617
- ```
618
-
619
- 或者仅按需引入数学字体 `m`:
620
-
621
- ```javascript
622
- import "18s/m.css";
623
- ```
624
-
625
- ##### 方式 B:在 CSS 入口中引入
626
-
627
- ```css
628
- /* 引入合并后的字体 CSS(包含思源黑体 t、代码字体 c 及数学字体 m) */
629
- @import "18s/_.css";
630
- ```
631
-
632
- 或者仅按需引入数学字体 `m`:
633
-
634
- ```css
635
- @import "18s/m.css";
636
- ```
637
-
638
- ### Markdown 解析器插件
639
-
640
- #### 1. Marked 插件
641
-
642
- 使用 [`@webc.site/math-marked`](https://www.npmjs.com/package/@webc.site/math-marked) 自动解析并编译行内(`$...$`)与块级(`$$...$$`)公式:
643
-
644
- ```javascript
645
- import { marked } from "marked";
646
- import mathMarked from "@webc.site/math-marked";
647
-
648
- marked.use(mathMarked());
649
-
650
- const html = marked.parse("欧拉恒等式:$$e^{i\\pi} + 1 = 0$$");
651
- ```
652
-
653
- #### 2. Remark 插件
654
-
655
- 使用 [`@webc.site/math-remark`](https://www.npmjs.com/package/@webc.site/math-remark) 将 Unified/Remark AST 中的 `inlineMath` 与 `math` 节点替换为原生 MathML HTML:
656
-
657
- ```javascript
658
- import { unified } from "unified";
659
- import remarkParse from "remark-parse";
660
- import remarkMath from "remark-math";
661
- import mathRemark from "@webc.site/math-remark";
662
- import remarkHtml from "remark-html";
663
-
664
- const html = await unified()
665
- .use(remarkParse)
666
- .use(remarkMath)
667
- .use(mathRemark)
668
- .use(remarkHtml, { sanitize: false })
669
- .process("欧拉恒等式:$$e^{i\\pi} + 1 = 0$$");
670
- ```
671
-
672
- #### 3. Markdown-it 插件
150
+ ## 3. 设计思路
673
151
 
674
- 使用 [`@webc.site/math-markdown-it`](https://www.npmjs.com/package/@webc.site/math-markdown-it) 自动解析并渲染 `markdown-it` 中的行内与块级公式:
675
-
676
- ```javascript
677
- import markdownit from "markdown-it";
678
- import mathMarkdownIt from "@webc.site/math-markdown-it";
679
-
680
- const md = markdownit().use(mathMarkdownIt);
681
-
682
- const html = md.render("欧拉恒等式:$$e^{i\\pi} + 1 = 0$$");
683
- ```
684
-
685
- ## 功能特性
686
-
687
- - **功能完备**:通过数千个 KaTeX/MathJax 官方测试用例(详见 [extract](https://github.com/webc-site/math/tree/dev/extract))。
688
- - **健壮容错**:公式语法错误(如未闭合的 `\left`/`\right`)会自动捕获并降级显示原始公式,不抛出异常,防止应用崩溃。
689
- - **快速编译**:无需外部依赖,直接将 TeX 公式编译为语义化 MathML。
690
- - **Markdown 集成**:自动解析 Markdown 中的行内公式(`$ 公式 $`)和块级公式(`$$ 公式 $$`)。
691
- - **标准兼容**:输出现代浏览器原生支持的标准 MathML 元素。
692
-
693
- ## 支持的语法清单
694
-
695
- 本库在极简的包体积下,支持最常用的数学公式排版语法:
696
-
697
- - **基础算术与符号**:数字、英文字母、基础运算符(`+`, `-`, `*`, `/`, `=`, `<`, `>`, `(`, `)`, `[`, `]`, `.`)。其中 `-` 自动映射为减号 `\u2212`,`*` 映射为星号 `\u2217`,`/` 以直立体呈现。
698
- - **上下标**:
699
- - 上标 `^`(如 `x^2`)
700
- - 下标 `_`(如 `x_i`)
701
- - 同时存在上下标(如 `x_i^2` 或 `x^2_i`)
702
- - 对于求和与积分等大型运算符,上下标会自动以限位上下标形式呈现(如 `\sum_{i=1}^n`,`\int_a^b`)
703
- - **分式**:`\frac{分子}{分母}`(如 `\frac{a}{b}`)
704
- - **开根号**:平方根 `\sqrt{x}` 以及 $n$ 次方根 `\sqrt[n]{x}`
705
- - **上划线与横线**:`\overline{x}` 及简写形式 `\bar{x}`
706
- - **自适应括号与定界符**:`\left` 和 `\right` 结构(如 `\left( ... \right)`)。支持的定界符包括:
707
- - 圆括号:`(` 和 `)`
708
- - 方括号:`[` 和 `]`
709
- - 花括号:`\{` 和 `\}`
710
- - 尖括号:`<` 和 `>`
711
- - 竖线:`|` 或 `\|`
712
- - 空白定界符:`.`(表示不显示该侧定界符,例如 `\left. \frac{df}{dx} \right| _0`)
713
- - **文本模式**:`\text{...}`(如 `\text{if }`),提取大括号中的字面文本,渲染为 MathML `<mtext>`,以常规直立体呈现。
714
- - **水平间距**:支持 `\quad`(1em 间距)和 `\qquad`(2em 间距)的空格占位。
715
- - **样式、删除线与占位隐藏**:
716
- - 边框:`\boxed{...}`(在公式周围加上边框,如 `\boxed{x+y}`)
717
- - 删除线与取消线:`\cancel{...}`(通过斜线划掉,如 `\cancel{x}`)和 `\sout{...}`(通过水平线划掉,如 `\sout{y}`)
718
- - 隐藏与占位:`\phantom{...}`(生成与输入内容相同宽高的不可见占位空间,如 `\phantom{x}`)
719
- - **常用数学函数**:`\sin`, `\cos`, `\tan`, `\cot`, `\sec`, `\csc`, `\log`, `\lg`, `\ln`, `\lim`, `\exp`, `\max`, `\min`, `\sup`, `\inf`, `\det`, `\gcd`, `\arcsin`, `\arccos`, `\arctan`, `\sinh`, `\cosh`, `\tanh`, `\coth`, `\deg`, `\arg`。其中 `\lim`, `\max`, `\min`, `\sup`, `\inf` 作为极限算子,其上下标定位在行间/块级公式中会自动以 limits 形式显示在算子正下方。
720
- - **同余括号**:`\pmod{...}`(生成带括号的同余占位,如 `\pmod{m}` 渲染为 $(mod\ m)$)
721
- - **希腊字母**:
722
- - 小写希腊字母:`\alpha` ($\alpha$), `\beta` ($\beta$), `\gamma` ($\gamma$), `\delta` ($\delta$), `\epsilon` ($\epsilon$), `\zeta` ($\zeta$), `\eta` ($\eta$), `\theta` ($\theta$), `\iota` ($\iota$), `\kappa` ($\kappa$), `\lambda` ($\lambda$), `\mu` ($\mu$), `\nu` ($\nu$), `\xi` ($\xi$), `\pi` ($\pi$), `\rho` ($\rho$), `\sigma` ($\sigma$), `\tau` ($\tau$), `\upsilon` ($\upsilon$), `\phi` ($\phi$), `\chi` ($\chi$), `\psi` ($\psi$), `\omega` ($\omega$)
723
- - 大写希腊字母(以直立体呈现):`\Delta` ($\Delta$), `\Gamma` ($\Gamma$), `\Theta` ($\Theta$), `\Lambda` ($\Lambda$), `\Xi` ($\Xi$), `\Pi` ($\Pi$), `\Sigma` ($\Sigma$), `\Upsilon` ($\Upsilon$), `\Phi` ($\Phi$), `\Psi` ($\Psi$), `\Omega` ($\Omega$)
724
- - **算术运算符与关系符**:
725
- - `\le` / `\leq` ($\le$), `\ge` / `\geq` ($\ge$), `\ne` / `\neq` ($\ne$)
726
- - `\cdot` ($\cdot$), `\times` ($\times$), `\pm` ($\pm$), `\mp` ($\mp$), `\div` ($\div$), `\infty` ($\infty$)
727
- - `\approx` ($\approx$), `\sim` ($\sim$), `\cong` ($\cong$), `\propto` ($\propto$), `\equiv` ($\equiv$), `\perp` ($\perp$), `\parallel` ($\parallel$)
728
- - **微积分、集合与逻辑符号**:
729
- - 梯度:`\nabla` ($\nabla$),偏微分:`\partial` ($\partial$)
730
- - 逻辑量词与运算:`\forall` ($\forall$),`\exists` ($\exists$),`\neg` ($\neg$),`\land` ($\land$),`\lor` ($\lor$)
731
- - 集合关系:`\in` ($\in$),`\notin` ($\notin$),`\ni` ($\ni$),`\subset` ($\subset$),`\supset` ($\supset$),`\subseteq` ($\subseteq$),`\supseteq` ($\supseteq$)
732
- - 集合运算:`\cup` ($\cup$),`\cap` ($\cap$),空集:`\emptyset` ($\emptyset$)
733
- - 特殊变量与常量:`\ell` ($\ell$), `\hbar` ($\hbar$)
734
- - 大型运算符:求和 `\sum` ($\sum$),积分 `\int` ($\int$)
735
- - **箭头符号**:
736
- - 单向箭头:`\to` / `\rightarrow` ($\rightarrow$), `\leftarrow` / `\gets` ($\leftarrow$), `\Leftarrow` ($\Leftarrow$), `\Rightarrow` ($\Rightarrow$)
737
- - 双向箭头:`\leftrightarrow` ($\leftrightarrow$), `\Leftrightarrow` ($\Leftrightarrow$)
738
- - **省略号**:
739
- - 基线省略号:`\dots` / `\ldots` ($\dots$)
740
- - 居中省略号:`\cdots` ($\cdots$)
741
- - **矩阵与多行排版**:
742
- - 矩阵环境:`matrix`, `pmatrix`, `bmatrix`, `vmatrix`, `Vmatrix`(如 `\begin{pmatrix} a & b \\ c & d \end{pmatrix}`)
743
- - 方程组与条件分支:`cases`(如 `\begin{cases} x & x \ge 0 \\ -x & x < 0 \end{cases}`)
744
- - 通用数组排版:`array`
745
- - 换行与对齐:使用 `\\`、`\\*` 或 `\\[width]` 进行换行,以及使用 `&` 进行列对齐
746
-
747
- ## 不支持的语法
748
-
749
- 目前不支持以下 LaTeX 扩展、宏定义或样式微调指令:
750
-
751
- 1. **宏定义命令**:`\newcommand`, `\renewcommand`, `\providecommand`, `\gdef`, `\let` 等。
752
- 2. **背景色与高级边框**:`\colorbox`, `\fcolorbox`, `\cellcolor` 等(支持 `\boxed`)。
753
- 3. **其他删除线**:`\bcancel`, `\xcancel` 等(支持 `\cancel` 和 `\sout`)。
754
- 4. **高级布局与隐藏**:`\hphantom`, `\vphantom`, `\smash` 等(支持 `\phantom`)。
755
- 5. **化学公式扩展**:化学宏包 `\ce{...}` (mhchem)。
756
- 6. **代码/文本抄录**:`\verb` 等。
757
- 7. **高级上/下标定位**:`\sideset`, `\prescript`, `\cramped`, `\flatfrac` 等。
758
- 8. **公式编号与自定义标签**:`\tag`, `\newtagform`, `\usetagform` 等。
759
- 9. **任意运算符的限位强制调整**:除预设的 `\sum`, `\int` 等大型运算符和 `\lim` 等极限算子外,不支持对任意自定义命令或结构使用 `\limits` 强制限位。
760
-
761
- ## 错误处理与容错机制
762
-
763
- 使用 `@webc.site/math/md.js` 解析 Markdown 文本时,若包含非法 LaTeX 语法,内部会自动捕获错误并退化为原始公式文本(如 `$$x + \left( y$$`),不抛出 JS 异常。因此,调用时**无需**包裹 `try...catch`。
764
-
765
- 如直接调用 `@webc.site/math` 编译非法 LaTeX,则会抛出错误码数组(详见下表),建议使用 `try...catch` 包裹。
766
-
767
- ### 内部错误码
768
-
769
- | 错误码 | 常量名 | 含义 | 触发示例 |
770
- | :----: | :------------------ | :-------------------------------- | :--------------------------------- |
771
- | `0` | `ERR_EXTRA_END` | 多余或非法的 `\end` 指令 | `\end{matrix}` (无对应的 `\begin`) |
772
- | `1` | `ERR_MISSING_RIGHT` | `\left` 定界符缺少配对的 `\right` | `\left( x` |
773
- | `2` | `ERR_EXTRA_RIGHT` | 多余或非法的 `\right` 指令 | `x \right)` (无对应的 `\left`) |
774
- | `3` | `ERR_MISSING_BRACE` | 命令缺少必填的大括号参数 | `\text x` (缺少 `{}`) |
775
-
776
- ## 设计思路与调用流程
777
-
778
- 解析器处理输入的 Markdown 字符串,隔离 TeX 表达式,并将其转换为 MathML 结构。
152
+ 编译器从输入的 Markdown 文本中提取 TeX 公式,执行词法分析和语法分析,生成对应的语义化 MathML 标记。
779
153
 
780
154
  ```mermaid
781
155
  graph TD
782
- A[输入 Markdown] --> B{扫描器}
783
- B -- 普通文本 --> C[输出缓冲区]
784
- B -- TeX 公式 --> D[词法分析: 生成 Token]
785
- D --> E[语法分析: 生成 AST]
786
- E --> F[代码生成: 生成 MathML 标签]
787
- F --> G[语义包装]
788
- G --> H[MathML 输出]
789
- C --> I[最终 HTML]
790
- H --> I
156
+ Input[输入 Markdown] --> Scanner[扫描器: 定位定界符]
157
+ Scanner -- 普通文本 --> Buffer[输出缓冲区]
158
+ Scanner -- TeX 公式 --> Lexer[词法分析: 生成 Token]
159
+ Lexer --> Parser[语法分析: 生成 AST]
160
+ Parser --> Codegen[代码生成: 映射 MathML 标签]
161
+ Codegen --> Wrapper[语义包装]
162
+ Wrapper --> MathML[MathML 输出]
163
+ Buffer --> Output[最终 HTML]
164
+ MathML --> Output
791
165
  ```
792
166
 
793
- ### 模块运行流程
794
-
795
- 1. **扫描器**:扫描输入字符串,定位公式定界符(`$` 和 `$$`)。
796
- 2. **词法分析**:将 TeX 字符串分解为数字、变量、运算符和控制命令等 Token。
797
- 3. **语法分析**:将 Token 转换为抽象语法树(AST)节点,支持分式、上下标及预设数学函数。
798
- 4. **代码生成**:将 AST 节点映射为标准 XML 节点(`<mi>`、`<mo>`、`<mn>`、`<mfrac>`、`<msup>`、`<msub>`、`<msubsup>`)。
799
-
800
- ## 如何添加新语法
801
-
802
- 添加新的 LaTeX 语法支持需要按顺序修改以下四个核心部分:
803
-
804
- ### 1. 常量定义
805
-
806
- 定义相应的词法 Token、语法节点类型、函数名或符号映射表:
807
-
808
- - **Token 类型**:在 [const/TOK.js](https://github.com/webc-site/math/blob/dev/src/const/TOK.js) 中定义(如 `export const TOK_MY_CMD = ...`)。
809
- - **节点类型**:在 [const/TYPE.js](https://github.com/webc-site/math/blob/dev/src/const/TYPE.js) 中定义(如 `export const TYPE_MY_NODE = ...`)。
810
- - **环境定界符**:若添加新的环境(如新矩阵或括号类型),需在 [mathml.js](https://github.com/webc-site/math/blob/dev/src/mathml.js) 的 `ENV_DELIMS` 中配置其左右定界符。
811
- - **符号映射表**:如果是普通数学符号或简单命令,只需在 [const/SYM.js](https://github.com/webc-site/math/blob/dev/src/const/SYM.js) 的 `SYM_MAP` 中将命令名映射为对应的 Unicode 字符。
812
- - **数学函数名**:在 [const/FUNC.js](https://github.com/webc-site/math/blob/dev/src/const/FUNC.js) 的 `FUNC_NAMES` 集合中定义。
167
+ ## 4. 技术栈
813
168
 
814
- ### 2. 词法分析
169
+ - **运行环境**:Bun, Node.js
170
+ - **语法检查与格式化**:oxlint, oxfmt
171
+ - **构建工具**:Vite, Rolldown, Lightning CSS
815
172
 
816
- `lex(str)` 函数负责将 LaTeX 输入字符串切割为 Token 数组。它位于 [lex.js](https://github.com/webc-site/math/blob/dev/src/lex.js)。
817
-
818
- - 如果引入了新的特殊字符或不同结构,需要更新 `lex` 函数中的字符匹配逻辑,让其识别并向 `tokens` 数组中推送相应的 `TOK_*` 类型和其字面值。
819
-
820
- ### 3. 语法分析
821
-
822
- `parse(tokens, state)` 函数负责将 Token 转换为抽象语法树(AST)节点。它位于 [parse.js](https://github.com/webc-site/math/blob/dev/src/parse.js)。
823
-
824
- - **命令解析**:主要在 [parse.js](https://github.com/webc-site/math/blob/dev/src/parse.js) 的 `TOK_MAP[TOK_CMD]` 函数中处理。当解析到对应的 LaTeX 命令(如 `\mycmd`)时,读取其参数(可使用 `read(tokens, state_ref)` 或 `grab(tokens, state_ref)`),并返回一个表示该节点的数组:`[TYPE_MY_NODE, arg1, arg2]`。
825
-
826
- ### 4. 代码渲染
827
-
828
- `SHOW_MAP` 字典负责将 AST 节点转换为标准的 MathML 标签字符串。它位于 [mathml.js](https://github.com/webc-site/math/blob/dev/src/mathml.js)。
829
-
830
- - 注册新的节点渲染函数:`[TYPE_MY_NODE]: ([_, arg1, arg2]) => nest("mylabel", arg1, arg2)`,将节点数据格式化为对应的标准 MathML 标记。
831
-
832
- ## 技术堆栈
833
-
834
- - **运行环境**:Bun、Node.js
835
- - **构建与打包**:SWC(代码压缩)、Vite(演示页面)
836
- - **开发工具**:oxlint、oxfmt
837
-
838
- ## 目录结构
173
+ ## 5. 代码结构
839
174
 
840
175
  ```
841
176
  .
842
- ├── demo/ # 交互式演示网页
843
- ├── const/ # 常量(预设公式、多语言列表)
844
- ├── i18n/ # 语言翻译配置文件
845
- │ ├── index.js # 演示页面交互逻辑
846
- ├── index.pug # Pug HTML 模板
847
- │ └── style.styl # 演示页面配色样式
848
- ├── extract/ # 经典公式提取脚本(KaTeX / MathJax 测试用例提取)
849
- ├── lib/ # 编译分发目录
850
- │ ├── package.json # 精简版 package.json,用于 NPM 发布
851
- │ ├── README.md # 同步生成的发布版 README
852
- ├── mathml.js # 压缩版 JS (TeX 编译器)
853
- │ ├── mathml.js.map # 独立 SourceMap
854
- │ ├── md.js # 压缩版 JS (Markdown 解析器)
855
- │ └── md.js.map # 独立 SourceMap
856
- ├── src/ # 源代码目录
857
- │ ├── const/ # 常量定义(Token 类型、AST 节点类型、符号映射、函数名等)
858
- │ ├── lex.js # LaTeX 公式词法分析器
859
- │ ├── parse.js # LaTeX 公式语法分析器(生成 AST)
860
- │ ├── mathml.js # TeX-to-MathML 编译器
861
- │ └── md.js # Markdown 数学公式解析入口
177
+ ├── demo/ # 演示页面
178
+ ├── extract/ # 测试用例提取脚本
179
+ ├── lib/ # 编译产物目录
180
+ │ ├── mathml.js # 核心编译器(压缩版)
181
+ └── md.js # Markdown 公式解析器(压缩版)
182
+ ├── src/ # 源代码
183
+ ├── const/ # Token、AST 节点、符号和函数常量定义
184
+ ├── lex.js # LaTeX 词法分析器
185
+ │ ├── parse.js # LaTeX 语法分析器(生成 AST)
186
+ │ ├── mathml.js # TeX 至 MathML 核心编译器
187
+ └── md.js # Markdown 公式解析入口
862
188
  ├── sh/ # 脚本目录
863
- ├── bench/ # 评测脚本、共享工具库与历史数据
864
- │ │ ├── pk.js # 体积/性能对比评测与 SVG 生成脚本
865
- │ │ ├── self.js # 体积/性能回归测试脚本
866
- │ │ ├── util.js # 评测共享工具库
867
- │ │ ├── chart.js # SVG 柱状图生成组件
868
- │ │ └── history.yml # YAML 格式历史基准数据
869
- │ └── check.js # 语言文件校验脚本
870
- ├── dev.js # 启动 Vite 开发服务器脚本
871
- ├── dist.js # 打包发布辅助脚本(自动递增版本、渲染模板)
872
- ├── minify.js # 代码打包压缩脚本
873
- ├── package.json # 项目配置文件
874
- ├── README.md # 同步生成的根目录项目说明
875
- ├── README.mdt # 用于合并中英文及介绍文件的 Markdown 模板
876
- └── test.sh # 格式化、代码检查及测试运行脚本
189
+ └── bench/ # 性能基准测试与历史数据
190
+ └── test.sh # 代码规范检查与测试运行脚本
877
191
  ```
878
192
 
879
- ## 历史背景
880
-
881
- 传统的 Web 数学公式渲染多依赖 MathJax 或 KaTeX。这类库体积较大,需要加载大量 JS 文件并执行复杂的排版计算,容易导致页面渲染出现延迟与白屏。
193
+ ## 6. 历史故事
882
194
 
883
- MathML(数学标记语言)规范旨在通过浏览器原生支持渲染数学符号。2023年,Blink 引擎正式支持 MathML Core 标准,标志着 Chrome、Safari Firefox 等主流浏览器全面实现了原生的数学公式排版。
195
+ 在万维网联盟(W3C)早期历史中,MathML 曾被提议为 HTML5 标准数学排版规范。但因其实现复杂度高,各浏览器引擎对该规范的支持程度参差不齐。2013 年,Chromium 项目以系统架构和安全隐患为由移除了原有的 MathML 渲染实现。导致网页渲染公式时,必须依赖 MathJax KaTeX 等体积庞大的 JavaScript 排版库在前端进行复杂的样式布局和字符定位计算。
884
196
 
885
- 配合 `18s` 项目提供的数学字体 `m`(Latin Modern Math,源自高德纳的 Computer Modern 经典字体),`@webc.site/math` 编译器能够将 TeX 公式直接转换为原生的 MathML 元素,完全利用浏览器底层的排版能力,消除了对运行时排版库的依赖。
197
+ 十年后,即 2023 1 月,Chrome 109 重新引入了对 MathML Core 标准的原生支持。该标准精简并优化了数学渲染逻辑,使其在现代浏览器引擎中表现更为高效。随着 WebKit (Safari)、Gecko (Firefox) 和 Blink (Chrome/Edge) MathML Core 规范实现全面覆盖,前端不再需要引入重型的 JavaScript 排版引擎。该项目应运而生,在编译期将 LaTeX 直接转换为原生 MathML 标签,去除运行时排版库依赖。
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@webc.site/math","version":"0.1.20","description":"The world's smallest and fastest web Markdown formula renderer / 全球最小最快的网页Markdown公式渲染器","keywords":["markdown","math","mathml","render","tex"],"homepage":"https://math.webc.site","license":"MulanPSL-2.0","author":"i18n.site@gmail.com","repository":{"type":"git","url":"git+https://github.com/webc-site/math.git"},"type":"module","exports":{".":{"types":"./mathml.d.ts","default":"./mathml.js"},"./md.js":{"types":"./md.d.ts","default":"./md.js"},"./*":"./*"},"dependencies":{}}
1
+ {"name":"@webc.site/math","version":"0.1.22","description":"The world's smallest and fastest web Markdown formula renderer / 全球最小最快的网页Markdown公式渲染器","keywords":["markdown","math","mathml","render","tex"],"homepage":"https://math.webc.site","license":"MulanPSL-2.0","author":"i18n.site@gmail.com","repository":{"type":"git","url":"git+https://github.com/webc-site/math.git"},"type":"module","exports":{".":{"types":"./mathml.d.ts","default":"./mathml.js"},"./md.js":{"types":"./md.d.ts","default":"./md.js"},"./*":"./*"},"dependencies":{}}