-
Notifications
You must be signed in to change notification settings - Fork 1
Added MathJax rendering #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ca4343b
c862d05
f5f8afa
ebba48c
e23407e
4a6bef4
ea17216
95cf825
11f8f6d
7df51b4
1a83f38
9c795cd
9d933cd
b2c972e
4ca1d21
f785fb5
b1959dd
98b61bc
827c7b1
4dfeb56
6e50a48
2f94afa
bce4b11
c787d5a
d03c36b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
<View> | ||
<HyperTextLabels name="ner" toName="text"> | ||
<Label value="Person"></Label> | ||
<Label value="Organization"></Label> | ||
<Label value="Date"></Label> | ||
<Label value="Paragraph" granularity="paragraph"></Label> | ||
<Label value="Word" granularity="word"></Label> | ||
<Label value="Div" granularity="div"></Label> | ||
<Label value="Interaction" granularity="parent_div"></Label> | ||
</HyperTextLabels> | ||
<TableText name="text" value="$text"></TableText> | ||
</View> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,12 +18,31 @@ | |
|
||
import React from 'react'; | ||
import { types } from 'mobx-state-tree'; | ||
import { MathJax, MathJaxContext } from 'better-react-mathjax'; | ||
import { cn } from '../../../utils/bem'; | ||
|
||
import './RichText.styl'; | ||
import { RichTextModel } from './model'; | ||
import { HtxRichText } from './view'; | ||
|
||
// To allow us to render MathJax expressions, we need to use a marker | ||
// However we also want to keep the original \(expressions\), so we swap into | ||
// a differnet marker instead. | ||
// We use a different marker than what is used in Khanmigo. | ||
const MATHJAX_MARKER = '$'; | ||
|
||
// Extract math from conversation, alternate between math and non-math | ||
// Khanmigo uses "\(.*?\)" as the marker for math | ||
// For example, "What is \(2 + 2\)?" will split into ["What is ", "2 + 2", "?"] | ||
const parseConvoWithMath = (str) => { | ||
// About the capture group: a cool behaviour of str.split is that if there's | ||
// capturing group, the group is captured into the group, which is perfect | ||
// for us! | ||
const mathRegex = /\\\((.*?)\\\)/g; | ||
|
||
return str.split(mathRegex); | ||
}; | ||
|
||
const renderTableValue = (val) => { | ||
let conversations = []; | ||
|
||
|
@@ -38,27 +57,114 @@ const renderTableValue = (val) => { | |
return <div className={errClass}>{errMsg}</div>; | ||
} | ||
|
||
const itemClass = cn('richtext', { elem: 'table-item' }); | ||
const questionItemClass = cn('richtext', { elem: 'table-item', mod: { qa : 'question' } }); | ||
const answerItemClass = cn('richtext', { elem: 'table-item', mod: { qa : 'answer' } }); | ||
let hasMath = false; | ||
|
||
const rowElems = conversations.map((conversation, index) => { | ||
const question = conversation[0]; | ||
const answer = conversation[1]; | ||
const mathQuestions = parseConvoWithMath(question); | ||
const mathAnswers = parseConvoWithMath(answer); | ||
let mathQuestionComponent = null; | ||
let mathAnswerComponent = null; | ||
|
||
// Render an alternate list between Math and non-math expressions | ||
// The list alternates between non-Math and Math expressions from | ||
// `parseConvoWithMath` | ||
const renderAllMathJax = (convoAndMathList) => ( | ||
convoAndMathList.map((convo, i) => { | ||
if (i % 2 === 0) { | ||
// Non math | ||
return <span key={`eq=${i}`}>{convo}</span>; | ||
} else { | ||
// So for Math, we need to create a span as we want 2 piece of dom: | ||
// 1. The hidden raw MathJax expression, to allow slot Label to work | ||
// 2. A marked MathJax expression that allows <MathJax/> to render | ||
return ( | ||
<span key={`eq-${i}`}> | ||
<span style={{ 'display': 'none' }}>{'\\(' + convo + '\\)'}</span> | ||
<span data-skip-select='1'>{MATHJAX_MARKER + convo + MATHJAX_MARKER}</span> | ||
</span> | ||
); | ||
} | ||
}) | ||
); | ||
|
||
if (mathQuestions.length > 1) { | ||
mathQuestionComponent = ( | ||
<div className={questionItemClass}> | ||
<MathJax dynamic>{renderAllMathJax(mathQuestions)}</MathJax> | ||
</div> | ||
); | ||
hasMath = true; | ||
} else if (question) { | ||
mathQuestionComponent = <div className={questionItemClass}>{question}</div>; | ||
} | ||
if (mathAnswers.length > 1) { | ||
mathAnswerComponent = ( | ||
<div className={answerItemClass}> | ||
<MathJax dynamic>{renderAllMathJax(mathAnswers)}</MathJax> | ||
</div> | ||
); | ||
hasMath = true; | ||
} else if (answer){ | ||
mathAnswerComponent = <div className={answerItemClass}>{answer}</div>; | ||
} | ||
|
||
return ( | ||
<div key={`conversation-${index}`}> | ||
<div className={itemClass}>{question}</div> | ||
<div className={itemClass}>{answer}</div> | ||
{mathQuestionComponent} | ||
{mathAnswerComponent} | ||
</div> | ||
); | ||
}); | ||
|
||
if (hasMath) { | ||
const mathJaxConfig = { | ||
tex: { | ||
inlineMath: [[MATHJAX_MARKER, MATHJAX_MARKER]], | ||
}, | ||
}; | ||
|
||
return ( | ||
<MathJaxContext config={mathJaxConfig}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs fixing, looks like I need to set the https://github.com/fast-reflexes/better-react-mathjax#dynamic-boolean--undefined There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/fast-reflexes/better-react-mathjax#rendermode-pre--post--undefined There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ha picking this up after almost a year! Thanks for the suggestions - I agree it sounds promising, but tried the |
||
{rowElems} | ||
</MathJaxContext> | ||
); | ||
} | ||
return <div>{rowElems}</div>; | ||
}; | ||
|
||
// We need to trigger MathJax typeset after the component is mounted | ||
// See https://docs.mathjax.org/en/latest/advanced/typeset.html | ||
// As the document suggest above, we need to ensure that only one typeSet | ||
// function is running at one time. We use the promise to ensure that the | ||
// typeset is only run once at a time. | ||
let typesetPromise = null; | ||
|
||
const triggerMathJaxTypeset = () => { | ||
dat-boris marked this conversation as resolved.
Show resolved
Hide resolved
|
||
setTimeout(() => { | ||
// This means that we already have a typeset running. | ||
if (typesetPromise) return; | ||
|
||
// This means that this is first load, and we can wait for | ||
// <MathJaxContext/> component to be ready and typeset, instead of doing | ||
// this dynamically. | ||
if (typeof window?.MathJax?.typesetPromise !== 'function') return; | ||
|
||
typesetPromise = window?.MathJax?.typesetPromise(); | ||
typesetPromise.finally(() => { | ||
typesetPromise = null; | ||
}); | ||
}, 100); | ||
}; | ||
|
||
export const TableText = () => ( | ||
HtxRichText({ | ||
isText: false, | ||
valueToComponent: renderTableValue, | ||
didMountCallback: triggerMathJaxTypeset, | ||
alwaysInline: true, | ||
}) | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I think I'm missing something - how come every other one is not math?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should explain this better in comment -
parseConvoWithMath
return a list of Math / non-math expression alternatively, which we wraps around here.