pantsdown 2.1.7 → 2.2.1
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.
- package/CLAUDE.md +72 -0
- package/package.json +15 -13
- package/src/css/styles.css +36 -2
- package/src/lexer.ts +18 -4
- package/src/parser.ts +8 -0
- package/src/renderer.ts +48 -5
- package/src/rules/block.ts +3 -1
- package/src/rules/inline.ts +4 -2
- package/src/tokenizer.ts +32 -0
- package/src/types.ts +14 -1
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
Pantsdown is a Markdown to HTML converter that renders markdown similar to GitHub's styling. It was built specifically for [github-preview.nvim](https://github.com/wallpants/github-preview.nvim). Based on [Marked](https://github.com/markedjs/marked).
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Type checking
|
|
13
|
+
bun run typecheck
|
|
14
|
+
|
|
15
|
+
# Linting
|
|
16
|
+
bun run lint
|
|
17
|
+
|
|
18
|
+
# Both typecheck and lint
|
|
19
|
+
bun run check
|
|
20
|
+
|
|
21
|
+
# Run tests (uses bun:test with happy-dom)
|
|
22
|
+
bun test
|
|
23
|
+
|
|
24
|
+
# Run a single test file
|
|
25
|
+
bun test tests/parse.test.ts
|
|
26
|
+
|
|
27
|
+
# Update test snapshots
|
|
28
|
+
bun test --update-snapshots
|
|
29
|
+
|
|
30
|
+
# Format code
|
|
31
|
+
bun run format
|
|
32
|
+
|
|
33
|
+
# Build docs
|
|
34
|
+
bun run docs:build
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Architecture
|
|
38
|
+
|
|
39
|
+
The parsing pipeline follows a classic compiler pattern:
|
|
40
|
+
|
|
41
|
+
1. **Lexer** (`src/lexer.ts`) - Tokenizes markdown source into an array of tokens
|
|
42
|
+
- Uses `Tokenizer` for the actual token creation
|
|
43
|
+
- Processes block-level tokens first, then inline tokens
|
|
44
|
+
- Tracks source maps for line number references
|
|
45
|
+
- Collects footnotes separately
|
|
46
|
+
|
|
47
|
+
2. **Tokenizer** (`src/tokenizer.ts`) - Creates tokens from markdown patterns
|
|
48
|
+
- Uses regex rules from `src/rules/block.ts` and `src/rules/inline.ts`
|
|
49
|
+
|
|
50
|
+
3. **Parser** (`src/parser.ts`) - Converts tokens to HTML by dispatching to the Renderer
|
|
51
|
+
- Recursively processes nested tokens
|
|
52
|
+
|
|
53
|
+
4. **Renderer** (`src/renderer.ts`) - Produces HTML output for each token type
|
|
54
|
+
- Uses highlight.js for syntax highlighting
|
|
55
|
+
- Uses github-slugger for heading anchors
|
|
56
|
+
- Handles special cases like mermaid diagrams and alerts
|
|
57
|
+
|
|
58
|
+
Entry point: `Pantsdown` class in `src/pantsdown.ts` coordinates the pipeline.
|
|
59
|
+
|
|
60
|
+
## Key Types
|
|
61
|
+
|
|
62
|
+
All token types are defined in `src/types.ts`. The `Token` union type covers all possible markdown elements (headings, code blocks, lists, tables, footnotes, alerts, etc.).
|
|
63
|
+
|
|
64
|
+
## Output
|
|
65
|
+
|
|
66
|
+
`pantsdown.parse(markdown)` returns `{ html, javascript }`:
|
|
67
|
+
- `html`: The rendered HTML string
|
|
68
|
+
- `javascript`: A script for interactive features (task list checkboxes, copy buttons)
|
|
69
|
+
|
|
70
|
+
## Styling
|
|
71
|
+
|
|
72
|
+
CSS is in `src/css/styles.css`. Requires a parent element with classes `pantsdown light` or `pantsdown dark` (optionally with `high-contrast`).
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "pantsdown",
|
|
3
3
|
"author": "wallpants",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "2.1
|
|
5
|
+
"version": "2.2.1",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"description": "Markdown to 'GitHub HTML' parser",
|
|
8
8
|
"repository": {
|
|
@@ -51,21 +51,23 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"github-slugger": "^2.0.0",
|
|
54
|
-
"highlight.js": "^11.11.1"
|
|
54
|
+
"highlight.js": "^11.11.1",
|
|
55
|
+
"katex": "^0.16.28"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
|
-
"@commitlint/config-conventional": "^19.
|
|
58
|
-
"@commitlint/cz-commitlint": "^19.
|
|
59
|
-
"@happy-dom/global-registrator": "^16.
|
|
60
|
-
"@types/bun": "^1.
|
|
61
|
-
"@
|
|
62
|
-
"@typescript-eslint/
|
|
58
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
59
|
+
"@commitlint/cz-commitlint": "^19.8.1",
|
|
60
|
+
"@happy-dom/global-registrator": "^16.8.1",
|
|
61
|
+
"@types/bun": "^1.3.7",
|
|
62
|
+
"@types/katex": "^0.16.8",
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
64
|
+
"@typescript-eslint/parser": "^8.54.0",
|
|
63
65
|
"commitizen": "^4.3.1",
|
|
64
|
-
"commitlint": "^19.
|
|
66
|
+
"commitlint": "^19.8.1",
|
|
65
67
|
"eslint": "^8.57.1",
|
|
66
|
-
"prettier": "^3.
|
|
67
|
-
"prettier-plugin-organize-imports": "^4.
|
|
68
|
-
"semantic-release": "^24.2.
|
|
69
|
-
"typescript": "5.
|
|
68
|
+
"prettier": "^3.8.1",
|
|
69
|
+
"prettier-plugin-organize-imports": "^4.3.0",
|
|
70
|
+
"semantic-release": "^24.2.9",
|
|
71
|
+
"typescript": "5.7.2"
|
|
70
72
|
}
|
|
71
73
|
}
|
package/src/css/styles.css
CHANGED
|
@@ -113,8 +113,9 @@
|
|
|
113
113
|
margin: 0;
|
|
114
114
|
color: var(--color-fg-default);
|
|
115
115
|
background-color: var(--color-canvas-default);
|
|
116
|
-
font-family:
|
|
117
|
-
|
|
116
|
+
font-family:
|
|
117
|
+
-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif,
|
|
118
|
+
"Apple Color Emoji", "Segoe UI Emoji";
|
|
118
119
|
font-size: 16px;
|
|
119
120
|
line-height: 1.5;
|
|
120
121
|
word-wrap: break-word;
|
|
@@ -1323,3 +1324,36 @@
|
|
|
1323
1324
|
/* .pantsdown.dark .hljs-tag { */
|
|
1324
1325
|
/* /1* purposely ignored *1/ */
|
|
1325
1326
|
/* } */
|
|
1327
|
+
|
|
1328
|
+
/*
|
|
1329
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
1330
|
+
* *
|
|
1331
|
+
* KaTeX / LaTeX *
|
|
1332
|
+
* *
|
|
1333
|
+
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
1334
|
+
*/
|
|
1335
|
+
|
|
1336
|
+
.pantsdown .katex-block {
|
|
1337
|
+
display: block;
|
|
1338
|
+
margin: 16px 0;
|
|
1339
|
+
text-align: center;
|
|
1340
|
+
overflow-x: auto;
|
|
1341
|
+
overflow-y: hidden;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
.pantsdown .katex-inline {
|
|
1345
|
+
display: inline;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
.pantsdown .katex-error {
|
|
1349
|
+
color: var(--color-danger-fg);
|
|
1350
|
+
background-color: var(--color-canvas-subtle);
|
|
1351
|
+
padding: 0.2em 0.4em;
|
|
1352
|
+
border-radius: 6px;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
.pantsdown .katex-error code {
|
|
1356
|
+
color: inherit;
|
|
1357
|
+
background-color: transparent;
|
|
1358
|
+
padding: 0;
|
|
1359
|
+
}
|
package/src/lexer.ts
CHANGED
|
@@ -111,6 +111,13 @@ export class Lexer {
|
|
|
111
111
|
continue;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// latexBlock
|
|
115
|
+
if ((token = this.tokenizer.latexBlock(src))) {
|
|
116
|
+
src = src.substring(token.raw.length);
|
|
117
|
+
tokens.push(token);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
114
121
|
// fences
|
|
115
122
|
if ((token = this.tokenizer.fences(src))) {
|
|
116
123
|
src = src.substring(token.raw.length);
|
|
@@ -169,8 +176,8 @@ export class Lexer {
|
|
|
169
176
|
lastToken.text += "\n" + token.raw;
|
|
170
177
|
const lastInline = this.inlineQueue[this.inlineQueue.length - 1];
|
|
171
178
|
if (lastInline) lastInline.src = lastToken.text;
|
|
172
|
-
} else
|
|
173
|
-
this.links[token.tag]
|
|
179
|
+
} else {
|
|
180
|
+
this.links[token.tag] ??= {
|
|
174
181
|
href: token.href,
|
|
175
182
|
title: token.title,
|
|
176
183
|
};
|
|
@@ -215,7 +222,7 @@ export class Lexer {
|
|
|
215
222
|
if ((token = this.tokenizer.text(src))) {
|
|
216
223
|
src = src.substring(token.raw.length);
|
|
217
224
|
lastToken = tokens[tokens.length - 1];
|
|
218
|
-
if (lastToken
|
|
225
|
+
if (lastToken?.type === "text") {
|
|
219
226
|
lastToken.raw += "\n" + token.raw;
|
|
220
227
|
lastToken.text += "\n" + token.text;
|
|
221
228
|
this.inlineQueue.pop();
|
|
@@ -291,6 +298,13 @@ export class Lexer {
|
|
|
291
298
|
}
|
|
292
299
|
keepPrevChar = false;
|
|
293
300
|
|
|
301
|
+
// latexInline (before escape to handle \(...\) syntax)
|
|
302
|
+
if ((token = this.tokenizer.latexInline(src))) {
|
|
303
|
+
src = src.substring(token.raw.length);
|
|
304
|
+
tokens.push(token);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
294
308
|
// escape
|
|
295
309
|
if ((token = this.tokenizer.escape(src))) {
|
|
296
310
|
src = src.substring(token.raw.length);
|
|
@@ -391,7 +405,7 @@ export class Lexer {
|
|
|
391
405
|
}
|
|
392
406
|
keepPrevChar = true;
|
|
393
407
|
lastToken = tokens[tokens.length - 1];
|
|
394
|
-
if (lastToken
|
|
408
|
+
if (lastToken?.type === "text") {
|
|
395
409
|
lastToken.raw += token.raw;
|
|
396
410
|
lastToken.text += token.text;
|
|
397
411
|
} else {
|
package/src/parser.ts
CHANGED
|
@@ -184,6 +184,10 @@ export class Parser {
|
|
|
184
184
|
out += top ? this.renderer.paragraph(body, textToken.sourceMap) : body;
|
|
185
185
|
continue;
|
|
186
186
|
}
|
|
187
|
+
case "latexBlock": {
|
|
188
|
+
out += this.renderer.latexBlock(token.text, token.sourceMap);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
187
191
|
|
|
188
192
|
default: {
|
|
189
193
|
const errMsg = 'Token with "' + token.type + '" type was not found.';
|
|
@@ -253,6 +257,10 @@ export class Parser {
|
|
|
253
257
|
out += this.renderer.text(token.text);
|
|
254
258
|
break;
|
|
255
259
|
}
|
|
260
|
+
case "latexInline": {
|
|
261
|
+
out += this.renderer.latexInline(token.text);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
256
264
|
default: {
|
|
257
265
|
const errMsg = 'Token with "' + token.type + '" type was not found.';
|
|
258
266
|
throw new Error(errMsg);
|
package/src/renderer.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import GithubSlugger from "github-slugger";
|
|
2
2
|
import hljs from "highlight.js";
|
|
3
|
+
import katex from "katex";
|
|
3
4
|
import { type Pantsdown } from "./pantsdown.ts";
|
|
4
5
|
import { inline } from "./rules/inline.ts";
|
|
5
6
|
import { type HTMLAttrs, type SourceMap, type Tokens } from "./types.ts";
|
|
@@ -25,18 +26,24 @@ export class Renderer {
|
|
|
25
26
|
|
|
26
27
|
code(code: string, infostring: string | undefined, sourceMap: SourceMap): string {
|
|
27
28
|
const lang = /^\S*/.exec(infostring ?? "")?.[0];
|
|
28
|
-
code = code.replace(/\n$/, "")
|
|
29
|
+
code = code.replace(/\n$/, "");
|
|
29
30
|
|
|
30
31
|
const attrs: HTMLAttrs = [];
|
|
31
32
|
|
|
32
33
|
if (lang === "mermaid") {
|
|
33
34
|
attrs.push(["class", "mermaid-container mermaid"]);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
code = hljs.highlight(code, { language }).value;
|
|
37
|
-
code = `<code class="hljs language-${escape(language)}">${code}</code>`;
|
|
35
|
+
const result = `<pre style="position: relative;">` + code + "\n" + `</pre>`;
|
|
36
|
+
return injectHtmlAttributes(result, attrs, sourceMap);
|
|
38
37
|
}
|
|
39
38
|
|
|
39
|
+
if (lang === "math") {
|
|
40
|
+
return this.latexBlock(code, sourceMap);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const language = lang && hljs.getLanguage(lang) ? lang : "plaintext";
|
|
44
|
+
code = hljs.highlight(code + "\n", { language }).value;
|
|
45
|
+
code = `<code class="hljs language-${escape(language)}">${code}</code>`;
|
|
46
|
+
|
|
40
47
|
const result = `<pre style="position: relative;">` + code + `</pre>`;
|
|
41
48
|
return injectHtmlAttributes(result, attrs, sourceMap);
|
|
42
49
|
}
|
|
@@ -212,4 +219,40 @@ export class Renderer {
|
|
|
212
219
|
text(text: string): string {
|
|
213
220
|
return text;
|
|
214
221
|
}
|
|
222
|
+
|
|
223
|
+
latexBlock(latex: string, sourceMap: SourceMap): string {
|
|
224
|
+
try {
|
|
225
|
+
const rendered = katex.renderToString(latex, {
|
|
226
|
+
displayMode: true,
|
|
227
|
+
throwOnError: false,
|
|
228
|
+
output: "html",
|
|
229
|
+
trust: false,
|
|
230
|
+
});
|
|
231
|
+
return injectHtmlAttributes(
|
|
232
|
+
`<div class="katex-block">${rendered}</div>\n`,
|
|
233
|
+
[],
|
|
234
|
+
sourceMap,
|
|
235
|
+
);
|
|
236
|
+
} catch {
|
|
237
|
+
return injectHtmlAttributes(
|
|
238
|
+
`<div class="katex-block katex-error"><code>${escape(latex)}</code></div>\n`,
|
|
239
|
+
[],
|
|
240
|
+
sourceMap,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
latexInline(latex: string): string {
|
|
246
|
+
try {
|
|
247
|
+
const rendered = katex.renderToString(latex, {
|
|
248
|
+
displayMode: false,
|
|
249
|
+
throwOnError: false,
|
|
250
|
+
output: "html",
|
|
251
|
+
trust: false,
|
|
252
|
+
});
|
|
253
|
+
return `<span class="katex-inline">${rendered}</span>`;
|
|
254
|
+
} catch {
|
|
255
|
+
return `<span class="katex-inline katex-error"><code>${escape(latex)}</code></span>`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
215
258
|
}
|
package/src/rules/block.ts
CHANGED
|
@@ -16,7 +16,8 @@ type BlockRuleNames =
|
|
|
16
16
|
| "bullet"
|
|
17
17
|
| "listItemStart"
|
|
18
18
|
| "footnote"
|
|
19
|
-
| "paragraph"
|
|
19
|
+
| "paragraph"
|
|
20
|
+
| "latexBlock";
|
|
20
21
|
|
|
21
22
|
export const label = /(?!\s*\])(?:\\.|[^\[\]\\])+/;
|
|
22
23
|
|
|
@@ -136,4 +137,5 @@ export const block: Record<BlockRuleNames, RegExp> = {
|
|
|
136
137
|
paragraph: block_paragraph,
|
|
137
138
|
footnote: /^\[\^([^\]\n]+)\]:(?:[ \t]+|[\n]*?|$)([^\n]*?(?:\n|$)(?:[\n]*?[ ]{4,}[^\n]*)*)/,
|
|
138
139
|
text: /^[^\n]+/,
|
|
140
|
+
latexBlock: /^(?:\$\$([^$]*(?:\$(?!\$)[^$]*)*)\$\$|\\\[([\s\S]*?)\\\])/,
|
|
139
141
|
};
|
package/src/rules/inline.ts
CHANGED
|
@@ -19,7 +19,8 @@ type InlineRuleNames =
|
|
|
19
19
|
| "punctuation"
|
|
20
20
|
| "blockSkip"
|
|
21
21
|
| "footnoteRef"
|
|
22
|
-
| "backpedal"
|
|
22
|
+
| "backpedal"
|
|
23
|
+
| "latexInline";
|
|
23
24
|
|
|
24
25
|
// list of unicode punctuation marks, plus any missing characters from CommonMark spec
|
|
25
26
|
const punctuation = "\\p{P}\\p{S}";
|
|
@@ -112,7 +113,7 @@ const inline_backpedal =
|
|
|
112
113
|
const inline_del = /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/;
|
|
113
114
|
|
|
114
115
|
const inline_text =
|
|
115
|
-
/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/;
|
|
116
|
+
/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_$]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/;
|
|
116
117
|
|
|
117
118
|
const inline_url = edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/, "i")
|
|
118
119
|
.replace("email", extended_email)
|
|
@@ -139,4 +140,5 @@ export const inline: Omit<Record<InlineRuleNames, RegExp>, "emStrong"> & {
|
|
|
139
140
|
blockSkip: inline_blockSkip,
|
|
140
141
|
backpedal: inline_backpedal,
|
|
141
142
|
footnoteRef: /^\[\^([^\]\n]+)\]/,
|
|
143
|
+
latexInline: /^(?:\$(?!\$)([^\s$](?:[^$\n]*[^\s$])?)\$(?!\$)|\\\((.+?)\\\))/,
|
|
142
144
|
};
|
package/src/tokenizer.ts
CHANGED
|
@@ -716,6 +716,7 @@ export class Tokenizer {
|
|
|
716
716
|
|
|
717
717
|
if (!nextChar || !prevChar || inline.punctuation.exec(prevChar)) {
|
|
718
718
|
// unicode Regex counts emoji as 1 char; spread into array for proper count (used multiple times below)
|
|
719
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-spread
|
|
719
720
|
const lLength = [...match[0]].length - 1;
|
|
720
721
|
let rDelim,
|
|
721
722
|
rLength,
|
|
@@ -736,6 +737,7 @@ export class Tokenizer {
|
|
|
736
737
|
|
|
737
738
|
if (!rDelim) continue; // skip single * in __abc*abc__
|
|
738
739
|
|
|
740
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-spread
|
|
739
741
|
rLength = [...rDelim].length;
|
|
740
742
|
|
|
741
743
|
if (match[3] || match[4]) {
|
|
@@ -757,6 +759,7 @@ export class Tokenizer {
|
|
|
757
759
|
// Remove extra characters. *a*** -> *a*
|
|
758
760
|
rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
|
|
759
761
|
// char length can be >1 for unicode characters;
|
|
762
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-spread
|
|
760
763
|
const lastCharLength = [...match[0]][0]!.length;
|
|
761
764
|
const raw = src.slice(0, lLength + match.index + lastCharLength + rLength);
|
|
762
765
|
|
|
@@ -920,4 +923,33 @@ export class Tokenizer {
|
|
|
920
923
|
text,
|
|
921
924
|
};
|
|
922
925
|
}
|
|
926
|
+
|
|
927
|
+
latexBlock(src: string): Tokens["LatexBlock"] | undefined {
|
|
928
|
+
const cap = block.latexBlock.exec(src);
|
|
929
|
+
if (!cap) return undefined;
|
|
930
|
+
|
|
931
|
+
// cap[1] is from $$...$$ syntax, cap[2] is from \[...\] syntax
|
|
932
|
+
const text = cap[1] ?? cap[2] ?? "";
|
|
933
|
+
|
|
934
|
+
return {
|
|
935
|
+
type: "latexBlock",
|
|
936
|
+
raw: cap[0],
|
|
937
|
+
text: text.trim(),
|
|
938
|
+
sourceMap: this.lexer.getSourceMap(cap[0]),
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
latexInline(src: string): Tokens["LatexInline"] | undefined {
|
|
943
|
+
const cap = inline.latexInline.exec(src);
|
|
944
|
+
if (!cap) return undefined;
|
|
945
|
+
|
|
946
|
+
// cap[1] is from $...$ syntax, cap[2] is from \(...\) syntax
|
|
947
|
+
const text = cap[1] ?? cap[2] ?? "";
|
|
948
|
+
|
|
949
|
+
return {
|
|
950
|
+
type: "latexInline",
|
|
951
|
+
raw: cap[0],
|
|
952
|
+
text,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
923
955
|
}
|
package/src/types.ts
CHANGED
|
@@ -186,6 +186,17 @@ export interface Tokens extends Record<string, BaseToken> {
|
|
|
186
186
|
raw: string;
|
|
187
187
|
items: Tokens["Footnote"][];
|
|
188
188
|
};
|
|
189
|
+
LatexBlock: {
|
|
190
|
+
type: "latexBlock";
|
|
191
|
+
raw: string;
|
|
192
|
+
text: string;
|
|
193
|
+
sourceMap: SourceMap;
|
|
194
|
+
};
|
|
195
|
+
LatexInline: {
|
|
196
|
+
type: "latexInline";
|
|
197
|
+
raw: string;
|
|
198
|
+
text: string;
|
|
199
|
+
};
|
|
189
200
|
}
|
|
190
201
|
|
|
191
202
|
export type Token =
|
|
@@ -214,7 +225,9 @@ export type Token =
|
|
|
214
225
|
| Tokens["Alert"]
|
|
215
226
|
| Tokens["Footnote"]
|
|
216
227
|
| Tokens["FootnoteRef"]
|
|
217
|
-
| Tokens["Footnotes"]
|
|
228
|
+
| Tokens["Footnotes"]
|
|
229
|
+
| Tokens["LatexBlock"]
|
|
230
|
+
| Tokens["LatexInline"];
|
|
218
231
|
|
|
219
232
|
export type Links = Record<string, { href: string; title: string }>;
|
|
220
233
|
|