diff --git a/README.md b/README.md
index fa2196a..dcc1778 100644
--- a/README.md
+++ b/README.md
@@ -42,8 +42,10 @@ const content = seeMarkReactParse(markdown);
| ------------------------- | ------- | -------------- | ------------------------------------------------------------------ |
| enableLatex | boolean | true | When false, LaTeX expressions are not parsed as math. |
| enableAsciimath | boolean | true | When false, AsciiMath expressions are not parsed as math. |
-| enableNemeth | boolean | true | When false, the Nemeth braille math extension is disabled and `@…@` syntax is not parsed. |
+| enableNemeth | boolean | true | When false, the Nemeth braille math extension is disabled. |
| latexDelimiter | string | 'bracket' | The delimiter for LaTeX expressions. Options: 'bracket', 'dollar'. |
+| asciimathDelimiter | string | 'graveaccent' | The delimiter for AsciiMath expressions. Options: 'graveaccent', 'asciimath'. |
+| nemethDelimiter | string | 'at' | The delimiter for Nemeth braille expressions. Options: 'at' (`@…@`), 'nemeth' (`\n…\n`). |
| documentFormat | string | 'inline' | The format of the document. Options: 'inline', 'block'. |
| imageFiles | object | { [ID]: File } | A map of image IDs to File objects for image rendering. |
| shouldBuildImageObjectURL | boolean | false | should build image object URL. |
@@ -132,8 +134,10 @@ const toc = createTableOfContents(markdown);
| -------------- | ------- | ------------- | ------------------------------------------------------------------------------------------- |
| enableLatex | boolean | true | When false, LaTeX expressions are not parsed as math. |
| enableAsciimath | boolean | true | When false, AsciiMath expressions are not parsed as math. |
-| enableNemeth | boolean | true | When false, the Nemeth braille math extension is disabled and `@…@` syntax is not parsed. |
+| enableNemeth | boolean | true | When false, the Nemeth braille math extension is disabled. |
| latexDelimiter | string | 'bracket' | The delimiter for LaTeX expressions. Options: 'bracket', 'dollar'. Must match the renderer. |
+| asciimathDelimiter | string | 'graveaccent' | The delimiter for AsciiMath expressions. Options: 'graveaccent', 'asciimath'. Must match the renderer. |
+| nemethDelimiter | string | 'at' | The delimiter for Nemeth braille expressions. Options: 'at' (`@…@`), 'nemeth' (`\n…\n`). Must match the renderer. |
### Return value
diff --git a/package.json b/package.json
index fde1c7c..4894324 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@coseeing/see-mark",
- "version": "1.9.0",
+ "version": "1.10.0",
"description": "A markdown parser for a11y",
"main": "./lib/see-mark.cjs",
"files": [
diff --git a/src/markdown-processor/__snapshots__/markdown-processor.test.js.snap b/src/markdown-processor/__snapshots__/markdown-processor.test.js.snap
index 02241b2..58e43a9 100644
--- a/src/markdown-processor/__snapshots__/markdown-processor.test.js.snap
+++ b/src/markdown-processor/__snapshots__/markdown-processor.test.js.snap
@@ -9,7 +9,7 @@ exports[`markdownProcessor should handle list of math expressions, with list ite
\\n a\\n +\\n b\\n =\\n c\\n","svg":"","position":{"start":2,"end":11}}"
+ data-seemark-payload="{"math":"a+b=c","typed":"latex","mathMl":"","svg":"","position":{"start":2,"end":11}}"
/>
@@ -21,7 +21,7 @@ exports[`markdownProcessor should handle list of math expressions, with list ite
\\n c\\n −\\n b\\n =\\n a\\n","svg":"","position":{"start":15,"end":24}}"
+ data-seemark-payload="{"math":"c-b=a","typed":"latex","mathMl":"","svg":"","position":{"start":15,"end":24}}"
/>
@@ -66,7 +66,7 @@ exports[`markdownProcessor should handle nemeth braille math expressions 1`] = `
\\n \\n a\\n 2\\n \\n","svg":"","position":{"start":0,"end":5}}"
+ data-seemark-payload="{"math":"⠁⠘⠆","typed":"nemeth","latex":"a^2","mathMl":"","svg":"","position":{"start":0,"end":5}}"
/>
diff --git a/src/markdown-processor/markdown-processor.js b/src/markdown-processor/markdown-processor.js
index 6a02285..37632cc 100644
--- a/src/markdown-processor/markdown-processor.js
+++ b/src/markdown-processor/markdown-processor.js
@@ -16,7 +16,8 @@ import imageDisplayLink from './marked-extentions/image-display-link';
import iframe from './marked-extentions/iframe';
export const createMarkdownProcessor = (options = {}) => {
- const asciimathDelimiter = 'graveaccent';
+ const asciimathDelimiter = options.asciimathDelimiter || 'graveaccent';
+ const nemethDelimiter = options.nemethDelimiter || 'at';
const enableLatex = options.enableLatex !== false;
const enableAsciimath = options.enableAsciimath !== false;
@@ -25,6 +26,7 @@ export const createMarkdownProcessor = (options = {}) => {
return markedProcessorFactory({
latexDelimiter: options.latexDelimiter,
asciimathDelimiter,
+ nemethDelimiter,
documentFormat: options.documentFormat,
imageFiles: options.imageFiles,
shouldBuildImageObjectURL: options.shouldBuildImageObjectURL,
diff --git a/src/markdown-processor/markdown-processor.test.js b/src/markdown-processor/markdown-processor.test.js
index 85d82a1..fdfaa64 100644
--- a/src/markdown-processor/markdown-processor.test.js
+++ b/src/markdown-processor/markdown-processor.test.js
@@ -61,6 +61,59 @@ describe('markdownProcessor', () => {
expect(container).toMatchSnapshot();
});
+ it('should handle asciimath expressions with graveaccent delimiter', () => {
+ const markdownContent = '`a+b=c`';
+ const options = {
+ latexDelimiter: 'bracket',
+ documentFormat: 'inline',
+ imageFiles: {},
+ };
+
+ const result = markdownProcessor(markdownContent, options);
+
+ const container = createDOMFromHTML(result);
+
+ const mathEl = getElementByType(container, SUPPORTED_COMPONENT_TYPES.MATH);
+
+ expect(mathEl).toBeTruthy();
+
+ const payload = JSON.parse(
+ mathEl.getAttribute(SEE_MARK_PAYLOAD_DATA_ATTRIBUTES)
+ );
+
+ expect(payload.typed).toBe('asciimath');
+ expect(payload.math).toBe('a+b=c');
+ expect(payload.mathMl).toBeTruthy();
+ expect(payload.svg).toBeTruthy();
+ });
+
+ it('should handle asciimath expressions with asciimath delimiter', () => {
+ const markdownContent = '\\aa+b=c\\a';
+ const options = {
+ asciimathDelimiter: 'asciimath',
+ latexDelimiter: 'bracket',
+ documentFormat: 'inline',
+ imageFiles: {},
+ };
+
+ const result = markdownProcessor(markdownContent, options);
+
+ const container = createDOMFromHTML(result);
+
+ const mathEl = getElementByType(container, SUPPORTED_COMPONENT_TYPES.MATH);
+
+ expect(mathEl).toBeTruthy();
+
+ const payload = JSON.parse(
+ mathEl.getAttribute(SEE_MARK_PAYLOAD_DATA_ATTRIBUTES)
+ );
+
+ expect(payload.typed).toBe('asciimath');
+ expect(payload.math).toBe('a+b=c');
+ expect(payload.mathMl).toBeTruthy();
+ expect(payload.svg).toBeTruthy();
+ });
+
it('should handle nemeth braille math expressions', () => {
const markdownContent = '@⠁⠘⠆@';
const options = {
@@ -91,6 +144,34 @@ describe('markdownProcessor', () => {
expect(container).toMatchSnapshot();
});
+ it('should handle nemeth braille math expressions with backslash-n delimiter', () => {
+ const markdownContent = '\\n⠁⠘⠆\\n';
+ const options = {
+ latexDelimiter: 'bracket',
+ nemethDelimiter: 'nemeth',
+ documentFormat: 'inline',
+ imageFiles: {},
+ };
+
+ const result = markdownProcessor(markdownContent, options);
+
+ const container = createDOMFromHTML(result);
+
+ const mathEl = getElementByType(container, SUPPORTED_COMPONENT_TYPES.MATH);
+
+ expect(mathEl).toBeTruthy();
+
+ const payload = JSON.parse(
+ mathEl.getAttribute(SEE_MARK_PAYLOAD_DATA_ATTRIBUTES)
+ );
+
+ expect(payload.typed).toBe('nemeth');
+ expect(payload.latex).toBe('a^2');
+ expect(payload.math).toBe('⠁⠘⠆');
+ expect(payload.mathMl).toBeTruthy();
+ expect(payload.svg).toBeTruthy();
+ });
+
it('should not parse nemeth braille when enableNemeth is false', () => {
const markdownContent = '@⠁⠘⠆@';
const options = {
diff --git a/src/markdown-processor/marked-extentions/nemeth.js b/src/markdown-processor/marked-extentions/nemeth.js
index 0946513..bda535b 100644
--- a/src/markdown-processor/marked-extentions/nemeth.js
+++ b/src/markdown-processor/marked-extentions/nemeth.js
@@ -5,9 +5,12 @@ import { createRenderer } from './helpers';
import { SUPPORTED_COMPONENT_TYPES } from '../../shared/supported-components';
// ^@@
-const reNemeth = /^@([\u2800-\u28FF]+)@/;
+const reNemethAt = /^@([\u2800-\u28FF]+)@/;
+// ^\n\n (literal backslash+n)
+const reNemethN = /^\\n([\u2800-\u28FF]+)\\n/;
-const markedNemeth = ({ documentFormat }) => {
+const markedNemeth = ({ documentFormat, nemethDelimiter = 'at' }) => {
+ const reNemeth = nemethDelimiter === 'nemeth' ? reNemethN : reNemethAt;
const latex2mml = latex2mmlFactory({ htmlMathDisplay: documentFormat });
return {
@@ -16,6 +19,11 @@ const markedNemeth = ({ documentFormat }) => {
name: 'nemeth',
level: 'inline',
start(src) {
+ if (nemethDelimiter === 'nemeth') {
+ // literal \n followed by braille char
+ const result = src.match(/\\n(?=[\u2800-\u28FF])/);
+ return result?.index ?? Infinity;
+ }
// @ followed by braille char (disambiguates from @[ and @!)
const result = src.match(/@(?=[\u2800-\u28FF])/);
return result?.index ?? Infinity;
diff --git a/src/markdown-processor/marked-wrapper/marked-wrapper.js b/src/markdown-processor/marked-wrapper/marked-wrapper.js
index 8d32f7f..72674d1 100644
--- a/src/markdown-processor/marked-wrapper/marked-wrapper.js
+++ b/src/markdown-processor/marked-wrapper/marked-wrapper.js
@@ -7,6 +7,7 @@ const markedProcessorFactory = ({
enableAsciimath = true,
latexDelimiter,
asciimathDelimiter,
+ nemethDelimiter,
documentFormat,
imageFiles = {},
shouldBuildImageObjectURL = false,
@@ -31,6 +32,7 @@ const markedProcessorFactory = ({
enableAsciimath,
latexDelimiter,
asciimathDelimiter,
+ nemethDelimiter,
documentFormat,
imageFiles,
shouldBuildImageObjectURL,
diff --git a/src/table-of-contents/create-table-of-contents.js b/src/table-of-contents/create-table-of-contents.js
index 3953966..2fe80cf 100644
--- a/src/table-of-contents/create-table-of-contents.js
+++ b/src/table-of-contents/create-table-of-contents.js
@@ -34,6 +34,10 @@ function extractPlainText(inlineTokens = []) {
* Must match the value used by the markdown renderer so that math tokens
* inside headings are recognised and their text is correctly extracted.
* Defaults to 'bracket'.
+ * @param {string} [options.asciimathDelimiter] - AsciiMath delimiter style ('graveaccent', 'asciimath').
+ * Must match the value used by the markdown renderer. Defaults to 'graveaccent'.
+ * @param {string} [options.nemethDelimiter] - Nemeth delimiter style ('at', 'nemeth').
+ * Must match the value used by the markdown renderer. Defaults to 'at'.
* @returns {{ level: number, id: string, text: string }[]}
*
* @example
@@ -49,6 +53,8 @@ const createTableOfContents = (markdown, options = {}) => {
enableAsciimath: options.enableAsciimath !== false,
enableNemeth: options.enableNemeth !== false,
latexDelimiter: options.latexDelimiter ?? 'bracket',
+ asciimathDelimiter: options.asciimathDelimiter ?? 'graveaccent',
+ nemethDelimiter: options.nemethDelimiter ?? 'at',
});
const tokens = lexer(markdown);
const usedIds = new Map();
diff --git a/src/table-of-contents/create-table-of-contents.test.js b/src/table-of-contents/create-table-of-contents.test.js
index fb13b82..4dc2c7b 100644
--- a/src/table-of-contents/create-table-of-contents.test.js
+++ b/src/table-of-contents/create-table-of-contents.test.js
@@ -77,12 +77,10 @@ describe('createTableOfContents', () => {
expect(result[0].id).toBe('hello-world');
});
- // FIXME: backtick code spans conflict with the graveaccent AsciiMath delimiter.
- // With seemark's math extension registered, `npm install` is tokenised as a
- // math token instead of a native code span, so text extraction drops the
- // expression.
- it.skip('should strip code span markdown from text field', () => {
- const result = createTableOfContents('## Use `npm install`');
+ it('should strip code span markdown from text field', () => {
+ const result = createTableOfContents('## Use `npm install`', {
+ asciimathDelimiter: 'asciimath', // backtick code spans conflict with the graveaccent AsciiMath delimiter
+ });
expect(result[0].text).toBe('Use npm install');
expect(result[0].id).toBe('use-npm-install');