Skip to content

Commit 51385f7

Browse files
authored
Add micromark-extension-math to enable TeX block in Markdown (#5332)
* Add `micromark-extension-math` * Add entry * Add screenshot * Add link * Add tests * Change to approx * Whitelist transform for math
1 parent 8356902 commit 51385f7

File tree

10 files changed

+214
-4
lines changed

10 files changed

+214
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
6060
- Added new style options `borderAnimationColor1`, `borderAnimationColor2`, and `borderAnimationColor3` for customizing decorator colors, in PR [#5312](https://github.com/microsoft/BotFramework-WebChat/pull/5312)
6161
- Added `styleOptions.bubbleAttachmentMaxWidth`/`bubbleAttachmentMinWidth` and `styleOptions.bubbleMessageMaxWidth`/`bubbleMessageMinWidth`, in PR [#5321](https://github.com/microsoft/BotFramework-WebChat/pull/5321), by [@compulim](https://github.com/compulim)
6262
- (Experimental) Added more CSS variables support, in PR [#5321](https://github.com/microsoft/BotFramework-WebChat/pull/5321), by [@compulim](https://github.com/compulim)
63+
- Added MathML/TeX block support in Markdown via [`micromark-extension-math`](https://npmjs.com/package/micromark-extension-math) and [`katex`](https://katex.org/), in PR [#5332](https://github.com/microsoft/BotFramework-WebChat/pull/5332), by [@compulim](https://github.com/compulim)
6364

6465
### Changed
6566

__tests__/html2/markdown/math.html

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<!doctype html>
2+
<html lang="en-US">
3+
<head>
4+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
5+
<script crossorigin="anonymous" src="/test-harness.js"></script>
6+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
7+
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
8+
</head>
9+
10+
<body>
11+
<main id="webchat"></main>
12+
<script>
13+
run(async function () {
14+
await host.windowSize(640, 720, document.getElementById('webchat'));
15+
16+
const {
17+
WebChat: { renderWebChat }
18+
} = window; // Imports in UMD fashion.
19+
20+
const { directLine, store } = testHelpers.createDirectLineEmulator();
21+
22+
renderWebChat({ directLine, store }, document.getElementById('webchat'));
23+
24+
await pageConditions.uiConnected();
25+
26+
await directLine.emulateIncomingActivity({
27+
text: `On January 1, 2018, the closing stock price of Microsoft was **$85.95**. On December 1, 2021, the closing stock price was **$330.08**.
28+
29+
Using these prices, here's how you can calculate the value of your investment:
30+
31+
1. Determine the number of shares you could buy on 1/1/2018:
32+
33+
$$
34+
\\text{Number of shares} = \\frac{\\text{Investment amount}}{\\text{Stock price purchase date}} = \\frac{1000}{85.95}
35+
$$
36+
37+
2. Calculate the total value when selling the shares on 12/1/2021:
38+
39+
$$
40+
\\text{Total value} = \\text{Number of shares} \\times \\text{Stock price on sale date}
41+
$$
42+
43+
Let's do the math:
44+
45+
1. Number of shares you could buy on 1/1/2018:
46+
47+
$$
48+
\\text{Number of shares} = \\frac{1000}{85.95} \\approx 11.63\\text{ shares}
49+
$$
50+
51+
2. Total value when selling the shares on 12/1/2021:
52+
53+
$$
54+
\\text{Total value} = 11.63 \\times 330.08 \\approx \\$3839.63
55+
$$
56+
57+
So, if you invested $1000 in Microsoft on January 1, 2018, and sold the shares on December 1, 2021, your investment would be worth approximately **$3839.63**. Please note that this calculation does not account for dividends, taxes, or transaction fees, which could affect the final amount. If you need a more precise calculation including these factors, I recommend consulting a financial advisor or using a detailed investment calculator.
58+
`,
59+
type: 'message'
60+
});
61+
62+
await pageConditions.numActivitiesShown(1);
63+
64+
await host.snapshot('local');
65+
});
66+
</script>
67+
</body>
68+
</html>
115 KB
Loading

__tests__/html2/markdown/math2.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!doctype html>
2+
<html lang="en-US">
3+
<head>
4+
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
5+
<script crossorigin="anonymous" src="/test-harness.js"></script>
6+
<script crossorigin="anonymous" src="/test-page-object.js"></script>
7+
<script crossorigin="anonymous" src="/__dist__/webchat-es5.js"></script>
8+
</head>
9+
10+
<body>
11+
<main id="webchat"></main>
12+
<script>
13+
run(async function () {
14+
await host.windowSize(640, 720, document.getElementById('webchat'));
15+
16+
const {
17+
WebChat: { renderWebChat }
18+
} = window; // Imports in UMD fashion.
19+
20+
const { directLine, store } = testHelpers.createDirectLineEmulator();
21+
22+
renderWebChat({ directLine, store }, document.getElementById('webchat'));
23+
24+
await pageConditions.uiConnected();
25+
26+
await directLine.emulateIncomingActivity({
27+
text: `I've graphed the parametric equations you provided. Here's the result:
28+
29+
$$
30+
x = \\sin(t)(e^{\\cos(t)}-2\\cos(4t)-\\sin^{5}(\\frac{t}{12}))
31+
$$
32+
$$
33+
y = \\cos(t)(e^{\\cos(t)}-2\\cos(4t)-\\sin^{5}(\\frac{t}{12}))
34+
$$
35+
36+
The graph is a representation of these equations plotted over a range of ( t ) values from 0 to ( 2\\pi ).`,
37+
type: 'message'
38+
});
39+
40+
await pageConditions.numActivitiesShown(1);
41+
42+
await host.snapshot('local');
43+
});
44+
</script>
45+
</body>
46+
</html>
34.3 KB
Loading

jest.legacy.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const TRANSFORM_IGNORE_PACKAGES = [
1717
'micromark-extension-gfm-table',
1818
'micromark-extension-gfm-tagfilter',
1919
'micromark-extension-gfm-task-list-item',
20+
'micromark-extension-math',
2021
'micromark-factory-destination',
2122
'micromark-factory-label',
2223
'micromark-factory-space',

package-lock.json

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/bundle/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
"memoize-one": "6.0.0",
135135
"micromark": "^4.0.0",
136136
"micromark-extension-gfm": "^3.0.0",
137+
"micromark-extension-math": "^3.1.0",
137138
"microsoft-cognitiveservices-speech-sdk": "1.17.0",
138139
"prop-types": "15.8.1",
139140
"punycode": "2.3.1",

packages/bundle/src/markdown/renderMarkdown.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import {
77
} from 'botframework-webchat-component/internal';
88
import { micromark } from 'micromark';
99
import { gfm, gfmHtml } from 'micromark-extension-gfm';
10-
import { pre as respectCRLFPre } from './private/respectCRLF';
10+
import { math, mathHtml } from 'micromark-extension-math';
1111
import betterLinkDocumentMod, { BetterLinkDocumentModDecoration } from './private/betterLinkDocumentMod';
1212
import iterateLinkDefinitions from './private/iterateLinkDefinitions';
13+
import { pre as respectCRLFPre } from './private/respectCRLF';
1314

1415
const SANITIZE_HTML_OPTIONS = Object.freeze({
1516
allowedAttributes: {
@@ -56,7 +57,38 @@ const SANITIZE_HTML_OPTIONS = Object.freeze({
5657
'th',
5758
'thead',
5859
'tr',
59-
'ul'
60+
'ul',
61+
62+
// Followings are for MathML elements, from https://developer.mozilla.org/en-US/docs/Web/MathML.
63+
'annotation-xml',
64+
'annotation',
65+
'math',
66+
'merror',
67+
'mfrac',
68+
'mi',
69+
'mmultiscripts',
70+
'mn',
71+
'mo',
72+
'mover',
73+
'mpadded',
74+
'mphantom',
75+
'mprescripts',
76+
'mroot',
77+
'mrow',
78+
'ms',
79+
'mspace',
80+
'msqrt',
81+
'mstyle',
82+
'msub',
83+
'msubsup',
84+
'msup',
85+
'mtable',
86+
'mtd',
87+
'mtext',
88+
'mtr',
89+
'munder',
90+
'munderover',
91+
'semantics'
6092
],
6193
// Bug of https://github.com/apostrophecms/sanitize-html/issues/633.
6294
// They should not remove `alt=""` even though it is empty.
@@ -145,8 +177,12 @@ export default function render(
145177
// We need to handle links like cite:1 or other URL handlers.
146178
// And we will remove dangerous protocol during sanitization.
147179
allowDangerousProtocol: true,
148-
extensions: [gfm()],
149-
htmlExtensions: [gfmHtml()]
180+
extensions: [
181+
gfm(),
182+
// Disabling single dollar inline math block to prevent easy collision.
183+
math({ singleDollarTextMath: false })
184+
],
185+
htmlExtensions: [gfmHtml(), mathHtml({ output: 'mathml' })]
150186
});
151187

152188
// TODO: [P1] In some future, we should apply "better link" and "sanitization" outside of the Markdown engine.

packages/component/src/Styles/StyleSet/RenderMarkdown.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ export default function createMarkdownStyle() {
6565

6666
'& .webchat__render-markdown__pure-identifier::before': {
6767
content: "'['"
68+
},
69+
70+
'& math': {
71+
alignItems: 'center',
72+
display: 'flex',
73+
flexDirection: 'column'
6874
}
6975
}
7076
};

0 commit comments

Comments
 (0)