@pie-lib/math-input-mathlive 1.1.4 → 1.1.5-beta.10

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 (50) hide show
  1. package/README.md +14 -5
  2. package/lib/__tests__/composites.test.js +255 -0
  3. package/lib/__tests__/drop-in-compat.test.js +133 -0
  4. package/lib/__tests__/embedded-fonts.test.js +121 -0
  5. package/lib/__tests__/index.test.js +118 -0
  6. package/lib/__tests__/input-imperative.test.js +155 -0
  7. package/lib/__tests__/keypad-label.test.js +231 -0
  8. package/lib/__tests__/keypad-render.test.js +206 -0
  9. package/lib/__tests__/latex-bridge.test.js +386 -0
  10. package/lib/__tests__/mathlive-instance.test.js +419 -0
  11. package/lib/__tests__/mount-race.test.js +218 -0
  12. package/lib/__tests__/static-prompts.test.js +405 -0
  13. package/lib/__tests__/tmp-markup.test.js +26 -0
  14. package/lib/keypad/index.js +50 -28
  15. package/lib/keypad/index.js.map +1 -1
  16. package/lib/keys/geometry.js +7 -4
  17. package/lib/keys/geometry.js.map +1 -1
  18. package/lib/keys/misc.js +9 -2
  19. package/lib/keys/misc.js.map +1 -1
  20. package/lib/latex-bridge.js +207 -7
  21. package/lib/latex-bridge.js.map +1 -1
  22. package/lib/mathlive-instance.js +176 -12
  23. package/lib/mathlive-instance.js.map +1 -1
  24. package/lib/mf/common-styles.js +89 -10
  25. package/lib/mf/common-styles.js.map +1 -1
  26. package/lib/mf/index.js +12 -0
  27. package/lib/mf/index.js.map +1 -1
  28. package/lib/mf/input.js +34 -5
  29. package/lib/mf/input.js.map +1 -1
  30. package/lib/mf/static.js +107 -7
  31. package/lib/mf/static.js.map +1 -1
  32. package/package.json +3 -4
  33. package/src/__tests__/composites.test.jsx +220 -0
  34. package/src/__tests__/keypad-label.test.jsx +229 -2
  35. package/src/__tests__/keypad-render.test.jsx +123 -0
  36. package/src/__tests__/latex-bridge.test.js +159 -0
  37. package/src/__tests__/mathlive-instance.test.js +237 -0
  38. package/src/__tests__/mount-race.test.jsx +142 -0
  39. package/src/__tests__/static-prompts.test.jsx +364 -0
  40. package/src/keypad/index.jsx +33 -17
  41. package/src/keys/geometry.js +7 -2
  42. package/src/keys/misc.js +8 -2
  43. package/src/latex-bridge.js +223 -7
  44. package/src/mathlive-instance.js +196 -11
  45. package/src/mf/common-styles.js +97 -8
  46. package/src/mf/index.js +4 -0
  47. package/src/mf/input.jsx +42 -5
  48. package/src/mf/static.jsx +124 -6
  49. package/CHANGELOG.md +0 -30
  50. package/LICENSE.md +0 -5
@@ -1 +1 @@
1
- {"version":3,"file":"static.js","names":["_react","_interopRequireDefault","require","_propTypes","_styles","_debug","_mathliveInstance","_latexBridge","_commonStyles","_callSuper","t","o","e","_getPrototypeOf2","_possibleConstructorReturn2","_isNativeReflectConstruct","Reflect","construct","constructor","apply","Boolean","prototype","valueOf","call","ownKeys","r","Object","keys","getOwnPropertySymbols","filter","getOwnPropertyDescriptor","enumerable","push","_objectSpread","arguments","length","forEach","_defineProperty2","getOwnPropertyDescriptors","defineProperties","defineProperty","log","debug","Holder","styled","display","placeholderStyles","border","background","Static","exports","_React$Component","props","_this","_classCallCheck2","document","liveRegion","createElement","assign","style","position","width","height","marginTop","clip","overflow","setAttribute","body","appendChild","parentNode","removeChild","undefined","message","textContent","clearTimeout","announceTimeout","setTimeout","event","isDeleting","key","next","converted","some","id","before","promptValues","after","test","slice","announce","_this$props","onSubFieldChange","getFieldName","mathField","values","announceConversion","fields","innerFields","name","promptHandle","_this$props2","onSubFieldFocus","ids","getPrompts","focused","find","range","getPromptRange","selection","ranges","_range","_slicedToArray2","start","end","_selection$ranges$","selStart","handle","setInput","holderRef","React","createRef","state","markup","_inherits2","_createClass2","value","_componentDidMount","_asyncToGenerator2","_regenerator","mark","_callee","ml","wrap","_context","prev","isInteractive","loadMathLive","renderMarkup","abrupt","sent","current","MathfieldElement","macros","getMacros","readOnly","mathVirtualKeyboardPolicy","toMathLive","latex","addEventListener","onPromptInput","onPromptFocus","onKeyDown","createLiveRegion","stop","componentDidMount","componentDidUpdate","prevProps","componentWillUnmount","removeEventListener","remove","removeLiveRegion","fieldIds","indexOf","latexToMarkup","setState","_this2","out","fromMathLive","getPromptValue","_this3","setPromptValue","get","_this4","map","focus","blur","render","_this$props3","className","onFocus","onBlur","ref","dangerouslySetInnerHTML","__html","Component","PropTypes","string","isRequired","func"],"sources":["../../src/mf/static.jsx"],"sourcesContent":["import React from 'react';\nimport PropTypes from 'prop-types';\nimport { styled } from '@mui/material/styles';\nimport debug from 'debug';\nimport { loadMathLive, getMacros, latexToMarkup } from '../mathlive-instance';\nimport { toMathLive, fromMathLive, fieldIds } from '../latex-bridge';\nimport { placeholderStyles } from './common-styles';\n\nconst log = debug('pie-lib:math-input-mathlive:static');\n\nconst Holder = styled('span')({\n display: 'inline-block',\n // Shared static-math rules: muted empty-slot glyphs and svg containment.\n ...placeholderStyles,\n '& math-field': {\n display: 'inline-block',\n border: 'none',\n background: 'transparent',\n },\n '& math-field::part(virtual-keyboard-toggle)': {\n display: 'none',\n },\n '& math-field::part(menu-toggle)': {\n display: 'none',\n },\n});\n\n/**\n * Static math. Drop-in replacement for @pie-lib/math-input's `mq.Static`.\n *\n * Two modes, chosen automatically:\n *\n * - **display** (no answer blocks in the latex): rendered with\n * `convertLatexToMarkup`. No mathfield instance, so it is cheap enough for\n * keypad labels - the old implementation created a full MathQuill\n * StaticMath per button.\n *\n * - **interactive** (latex contains `\\MathQuillMathField[..]{}` /\n * `\\placeholder[..]{}`): a read-only `<math-field>` whose placeholders stay\n * editable. Sub-field values are surfaced through the prompts API, replacing\n * MathQuill's `innerFields`.\n */\nexport default class Static extends React.Component {\n static propTypes = {\n latex: PropTypes.string.isRequired,\n className: PropTypes.string,\n onFocus: PropTypes.func,\n onBlur: PropTypes.func,\n getFieldName: PropTypes.func,\n onSubFieldChange: PropTypes.func,\n onSubFieldFocus: PropTypes.func,\n setInput: PropTypes.func,\n };\n\n static defaultProps = {\n getFieldName: () => {},\n };\n\n constructor(props) {\n super(props);\n this.holderRef = React.createRef();\n this.state = { markup: '' };\n this.promptValues = {};\n }\n\n async componentDidMount() {\n if (!this.isInteractive()) {\n await loadMathLive();\n this.renderMarkup();\n return;\n }\n\n const ml = await loadMathLive();\n\n if (!ml || !this.holderRef.current) {\n return;\n }\n\n this.mathField = new ml.MathfieldElement({ macros: getMacros() });\n this.mathField.readOnly = true;\n this.mathField.mathVirtualKeyboardPolicy = 'manual';\n this.mathField.value = toMathLive(this.props.latex);\n\n this.mathField.addEventListener('input', this.onPromptInput);\n this.mathField.addEventListener('focusin', this.onPromptFocus);\n this.mathField.addEventListener('keydown', this.onKeyDown);\n\n this.holderRef.current.appendChild(this.mathField);\n\n this.createLiveRegion();\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.latex === this.props.latex) {\n return;\n }\n\n if (this.mathField) {\n this.mathField.value = toMathLive(this.props.latex);\n } else {\n this.renderMarkup();\n }\n }\n\n componentWillUnmount() {\n if (this.mathField) {\n this.mathField.removeEventListener('input', this.onPromptInput);\n this.mathField.removeEventListener('focusin', this.onPromptFocus);\n this.mathField.removeEventListener('keydown', this.onKeyDown);\n this.mathField.remove();\n this.mathField = undefined;\n }\n\n this.removeLiveRegion();\n }\n\n /**\n * Off-screen aria-live region, ported from @pie-lib/math-input's Static.\n *\n * MathLive has its own accessibility layer, but it does not announce that\n * typed input was converted into a math symbol, which screen-reader users\n * relied on in the MathQuill implementation.\n */\n createLiveRegion = () => {\n if (typeof document === 'undefined' || this.liveRegion) {\n return;\n }\n\n this.liveRegion = document.createElement('div');\n Object.assign(this.liveRegion.style, {\n position: 'absolute',\n width: '1px',\n height: '1px',\n marginTop: '-1px',\n clip: 'rect(1px, 1px, 1px, 1px)',\n overflow: 'hidden',\n });\n this.liveRegion.setAttribute('aria-live', 'polite');\n this.liveRegion.setAttribute('aria-atomic', 'true');\n\n document.body.appendChild(this.liveRegion);\n };\n\n removeLiveRegion = () => {\n if (this.liveRegion && this.liveRegion.parentNode) {\n this.liveRegion.parentNode.removeChild(this.liveRegion);\n }\n\n this.liveRegion = undefined;\n };\n\n announce = (message) => {\n if (!this.liveRegion) {\n return;\n }\n\n this.liveRegion.textContent = message;\n\n // Clear once announced, so the same message can be announced again.\n clearTimeout(this.announceTimeout);\n this.announceTimeout = setTimeout(() => {\n if (this.liveRegion) {\n this.liveRegion.textContent = '';\n }\n }, 500);\n };\n\n onKeyDown = (event) => {\n // Deletions should not be announced as conversions.\n this.isDeleting = event?.key === 'Backspace' || event?.key === 'Delete';\n };\n\n /**\n * Announce when typed input became a math symbol. The MathQuill version\n * inferred this by counting braces; here a prompt whose latex grew by more\n * than the single character typed means a command was expanded.\n */\n announceConversion = (next) => {\n if (this.isDeleting) {\n this.isDeleting = false;\n return;\n }\n\n const converted = Object.keys(next).some((id) => {\n const before = this.promptValues[id] || '';\n const after = next[id] || '';\n\n return after.length > before.length + 1 && /\\\\/.test(after.slice(before.length));\n });\n\n if (converted) {\n this.announce('Converted to math symbol');\n }\n };\n\n /** Does this latex contain answer blocks? */\n isInteractive() {\n const latex = this.props.latex || '';\n\n return fieldIds(latex).length > 0 || latex.indexOf('\\\\placeholder') !== -1;\n }\n\n renderMarkup() {\n const markup = latexToMarkup(this.props.latex);\n\n if (markup !== this.state.markup) {\n this.setState({ markup });\n }\n }\n\n /** MathQuill's `innerFields` equivalent, keyed by prompt id. */\n values() {\n if (!this.mathField) {\n return {};\n }\n\n const out = {};\n\n (this.mathField.getPrompts() || []).forEach((id) => {\n out[id] = fromMathLive(this.mathField.getPromptValue(id));\n });\n\n return out;\n }\n\n /**\n * A MathQuill-inner-field-like handle for a prompt.\n *\n * The old API handed callers a MathQuill field object, and consumers commonly\n * called `field.latex()` on it. There is no equivalent object in MathLive -\n * prompts are addressed by id - so this adapter exposes the parts that were\n * actually used: `id` and a get/set `latex()`.\n */\n promptHandle(id) {\n return {\n id,\n latex: (value) => {\n if (!this.mathField) {\n return '';\n }\n\n if (value === undefined) {\n return fromMathLive(this.mathField.getPromptValue(id));\n }\n\n this.mathField.setPromptValue(id, toMathLive(value), {});\n\n return value;\n },\n };\n }\n\n /** All prompt handles, the analogue of `mathField.innerFields`. */\n get innerFields() {\n if (!this.mathField) {\n return [];\n }\n\n return (this.mathField.getPrompts() || []).map((id) => this.promptHandle(id));\n }\n\n onPromptInput = () => {\n const { onSubFieldChange, getFieldName } = this.props;\n\n if (!this.mathField) {\n return;\n }\n\n const next = this.values();\n\n this.announceConversion(next);\n\n if (!onSubFieldChange) {\n this.promptValues = next;\n return;\n }\n\n const fields = this.innerFields;\n\n // Emit only the prompts whose value actually changed, mirroring the\n // per-field granularity of the old onSubFieldChange(name, latex).\n Object.keys(next).forEach((id) => {\n if (next[id] === this.promptValues[id]) {\n return;\n }\n\n const name = getFieldName(this.promptHandle(id), fields) || id;\n\n onSubFieldChange(name, next[id]);\n });\n\n this.promptValues = next;\n };\n\n onPromptFocus = () => {\n const { onSubFieldFocus, getFieldName } = this.props;\n\n if (!this.mathField || !onSubFieldFocus) {\n return;\n }\n\n try {\n const ids = this.mathField.getPrompts() || [];\n // The focused prompt is the one containing the current selection.\n const focused = ids.find((id) => {\n const range = this.mathField.getPromptRange(id);\n const selection = this.mathField.selection;\n\n if (!range || !selection || !selection.ranges || !selection.ranges.length) {\n return false;\n }\n\n const [start, end] = range;\n const [selStart] = selection.ranges[0];\n\n return selStart >= start && selStart <= end;\n });\n\n if (focused) {\n const handle = this.promptHandle(focused);\n const name = getFieldName(handle, this.innerFields) || focused;\n\n if (this.props.setInput) {\n this.props.setInput(handle);\n }\n\n onSubFieldFocus(name, handle);\n }\n } catch (e) {\n log('error resolving focused prompt: %s', e.message);\n }\n };\n\n focus() {\n this.mathField && this.mathField.focus();\n }\n\n blur() {\n this.mathField && this.mathField.blur();\n }\n\n render() {\n const { className, onFocus, onBlur } = this.props;\n const { markup } = this.state;\n\n if (this.mathField || this.isInteractive()) {\n return <Holder className={className} onFocus={onFocus} onBlur={onBlur} ref={this.holderRef} />;\n }\n\n return (\n <Holder\n className={className}\n onFocus={onFocus}\n onBlur={onBlur}\n ref={this.holderRef}\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: markup }}\n />\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"static.js","names":["_react","_interopRequireDefault","require","_propTypes","_styles","_debug","_mathliveInstance","_latexBridge","_commonStyles","_callSuper","t","o","e","_getPrototypeOf2","_possibleConstructorReturn2","_isNativeReflectConstruct","Reflect","construct","constructor","apply","Boolean","prototype","valueOf","call","ownKeys","r","Object","keys","getOwnPropertySymbols","filter","getOwnPropertyDescriptor","enumerable","push","_objectSpread","arguments","length","forEach","_defineProperty2","getOwnPropertyDescriptors","defineProperties","defineProperty","log","debug","Holder","styled","display","placeholderStyles","rootIndexStyles","parallelStyles","border","background","Static","exports","_React$Component","props","_this","_classCallCheck2","document","liveRegion","createElement","assign","style","position","width","height","marginTop","clip","overflow","setAttribute","body","appendChild","parentNode","removeChild","undefined","message","textContent","clearTimeout","announceTimeout","setTimeout","event","isDeleting","key","next","converted","some","id","before","promptValues","after","test","slice","announce","_this$props","onSubFieldChange","getFieldName","mathField","values","announceConversion","fields","innerFields","name","promptHandle","_this$props2","onSubFieldFocus","ids","getPrompts","focused","find","range","getPromptRange","selection","ranges","_range","_slicedToArray2","start","end","_selection$ranges$","selStart","handle","setInput","holderRef","React","createRef","state","markup","_inherits2","_createClass2","value","_componentDidMount","_asyncToGenerator2","_regenerator","mark","_callee","generation","ml","wrap","_context","prev","mountGeneration","isInteractive","loadMathLive","renderMarkup","abrupt","sent","current","MathfieldElement","macros","getMacros","readOnly","mathVirtualKeyboardPolicy","mathModeSpace","MATH_MODE_SPACE","smartSuperscript","toMathLive","latex","applyShadowStyles","untrackCaret","trackPlaceholderCaret","addEventListener","onPromptInput","onPromptFocus","onKeyDown","replaceChildren","createLiveRegion","stop","componentDidMount","componentDidUpdate","prevProps","componentWillUnmount","removeEventListener","remove","removeLiveRegion","fieldIds","indexOf","latexToMarkup","setState","_this2","out","fromMathLive","getPromptValue","_this3","focus","focusPrompt","setPromptValue","get","_this4","map","promptContaining","_this5","target","executeCommand","blur","render","_this$props3","className","onFocus","onBlur","ref","dangerouslySetInnerHTML","__html","Component","PropTypes","string","isRequired","func"],"sources":["../../src/mf/static.jsx"],"sourcesContent":["import React from 'react';\nimport PropTypes from 'prop-types';\nimport { styled } from '@mui/material/styles';\nimport debug from 'debug';\nimport {\n loadMathLive,\n getMacros,\n latexToMarkup,\n MATH_MODE_SPACE,\n applyShadowStyles,\n trackPlaceholderCaret,\n} from '../mathlive-instance';\nimport { toMathLive, fromMathLive, fieldIds } from '../latex-bridge';\nimport { placeholderStyles, rootIndexStyles, parallelStyles } from './common-styles';\n\nconst log = debug('pie-lib:math-input-mathlive:static');\n\nconst Holder = styled('span')({\n display: 'inline-block',\n // Shared static-math rules: muted empty-slot glyphs and svg containment.\n ...placeholderStyles,\n // Readable radical index - display mode renders in the light DOM, so the\n // shadow-root copy of this rule does not reach it.\n ...rootIndexStyles,\n ...parallelStyles,\n '& math-field': {\n display: 'inline-block',\n border: 'none',\n background: 'transparent',\n },\n '& math-field::part(virtual-keyboard-toggle)': {\n display: 'none',\n },\n '& math-field::part(menu-toggle)': {\n display: 'none',\n },\n});\n\n/**\n * Static math. Drop-in replacement for @pie-lib/math-input's `mq.Static`.\n *\n * Two modes, chosen automatically:\n *\n * - **display** (no answer blocks in the latex): rendered with\n * `convertLatexToMarkup`. No mathfield instance, so it is cheap enough for\n * keypad labels - the old implementation created a full MathQuill\n * StaticMath per button.\n *\n * - **interactive** (latex contains `\\MathQuillMathField[..]{}` /\n * `\\placeholder[..]{}`): a read-only `<math-field>` whose placeholders stay\n * editable. Sub-field values are surfaced through the prompts API, replacing\n * MathQuill's `innerFields`.\n */\nexport default class Static extends React.Component {\n static propTypes = {\n latex: PropTypes.string.isRequired,\n className: PropTypes.string,\n onFocus: PropTypes.func,\n onBlur: PropTypes.func,\n getFieldName: PropTypes.func,\n onSubFieldChange: PropTypes.func,\n onSubFieldFocus: PropTypes.func,\n setInput: PropTypes.func,\n };\n\n static defaultProps = {\n getFieldName: () => {},\n };\n\n constructor(props) {\n super(props);\n this.holderRef = React.createRef();\n this.state = { markup: '' };\n this.promptValues = {};\n }\n\n async componentDidMount() {\n // See the note in mf/input.jsx: a generation counter, not a boolean, so a\n // StrictMode mount -> unmount -> mount on the same instance still ends up\n // with exactly one field.\n const generation = (this.mountGeneration = (this.mountGeneration || 0) + 1);\n\n if (!this.isInteractive()) {\n await loadMathLive();\n\n if (generation === this.mountGeneration) {\n this.renderMarkup();\n }\n\n return;\n }\n\n const ml = await loadMathLive();\n\n if (generation !== this.mountGeneration || !ml || !this.holderRef.current || this.mathField) {\n return;\n }\n\n this.mathField = new ml.MathfieldElement({ macros: getMacros() });\n this.mathField.readOnly = true;\n this.mathField.mathVirtualKeyboardPolicy = 'manual';\n // Without this the spacebar does nothing: MathLive's mathModeSpace\n // defaults to an empty string.\n this.mathField.mathModeSpace = MATH_MODE_SPACE;\n // Same as the editor: stay in the exponent after a digit (see mf/input.jsx).\n this.mathField.smartSuperscript = false;\n this.mathField.value = toMathLive(this.props.latex);\n\n // The mathfield renders in shadow DOM, so page CSS cannot reach it - the\n // stretchy-accent fix has to be injected there directly.\n applyShadowStyles(this.mathField);\n\n // A selected `\\placeholder{}` gets no MathLive caret; draw one. Answer\n // blocks are typed into with the same keypad, so they grow placeholders too.\n this.untrackCaret = trackPlaceholderCaret(this.mathField);\n\n this.mathField.addEventListener('input', this.onPromptInput);\n this.mathField.addEventListener('focusin', this.onPromptFocus);\n this.mathField.addEventListener('keydown', this.onKeyDown);\n\n // Belt and braces: drop anything a previous mount may have left behind.\n this.holderRef.current.replaceChildren(this.mathField);\n\n this.createLiveRegion();\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.latex === this.props.latex) {\n return;\n }\n\n if (this.mathField) {\n this.mathField.value = toMathLive(this.props.latex);\n } else {\n this.renderMarkup();\n }\n }\n\n componentWillUnmount() {\n // Invalidate any mount still awaiting the MathLive load.\n this.mountGeneration = (this.mountGeneration || 0) + 1;\n\n if (this.untrackCaret) {\n this.untrackCaret();\n this.untrackCaret = undefined;\n }\n\n if (this.mathField) {\n this.mathField.removeEventListener('input', this.onPromptInput);\n this.mathField.removeEventListener('focusin', this.onPromptFocus);\n this.mathField.removeEventListener('keydown', this.onKeyDown);\n this.mathField.remove();\n this.mathField = undefined;\n }\n\n this.removeLiveRegion();\n }\n\n /**\n * Off-screen aria-live region, ported from @pie-lib/math-input's Static.\n *\n * MathLive has its own accessibility layer, but it does not announce that\n * typed input was converted into a math symbol, which screen-reader users\n * relied on in the MathQuill implementation.\n */\n createLiveRegion = () => {\n if (typeof document === 'undefined' || this.liveRegion) {\n return;\n }\n\n this.liveRegion = document.createElement('div');\n Object.assign(this.liveRegion.style, {\n position: 'absolute',\n width: '1px',\n height: '1px',\n marginTop: '-1px',\n clip: 'rect(1px, 1px, 1px, 1px)',\n overflow: 'hidden',\n });\n this.liveRegion.setAttribute('aria-live', 'polite');\n this.liveRegion.setAttribute('aria-atomic', 'true');\n\n document.body.appendChild(this.liveRegion);\n };\n\n removeLiveRegion = () => {\n if (this.liveRegion && this.liveRegion.parentNode) {\n this.liveRegion.parentNode.removeChild(this.liveRegion);\n }\n\n this.liveRegion = undefined;\n };\n\n announce = (message) => {\n if (!this.liveRegion) {\n return;\n }\n\n this.liveRegion.textContent = message;\n\n // Clear once announced, so the same message can be announced again.\n clearTimeout(this.announceTimeout);\n this.announceTimeout = setTimeout(() => {\n if (this.liveRegion) {\n this.liveRegion.textContent = '';\n }\n }, 500);\n };\n\n onKeyDown = (event) => {\n // Deletions should not be announced as conversions.\n this.isDeleting = event?.key === 'Backspace' || event?.key === 'Delete';\n };\n\n /**\n * Announce when typed input became a math symbol. The MathQuill version\n * inferred this by counting braces; here a prompt whose latex grew by more\n * than the single character typed means a command was expanded.\n */\n announceConversion = (next) => {\n if (this.isDeleting) {\n this.isDeleting = false;\n return;\n }\n\n const converted = Object.keys(next).some((id) => {\n const before = this.promptValues[id] || '';\n const after = next[id] || '';\n\n return after.length > before.length + 1 && /\\\\/.test(after.slice(before.length));\n });\n\n if (converted) {\n this.announce('Converted to math symbol');\n }\n };\n\n /** Does this latex contain answer blocks? */\n isInteractive() {\n const latex = this.props.latex || '';\n\n return fieldIds(latex).length > 0 || latex.indexOf('\\\\placeholder') !== -1;\n }\n\n renderMarkup() {\n const markup = latexToMarkup(this.props.latex);\n\n if (markup !== this.state.markup) {\n this.setState({ markup });\n }\n }\n\n /** MathQuill's `innerFields` equivalent, keyed by prompt id. */\n values() {\n if (!this.mathField) {\n return {};\n }\n\n const out = {};\n\n (this.mathField.getPrompts() || []).forEach((id) => {\n out[id] = fromMathLive(this.mathField.getPromptValue(id));\n });\n\n return out;\n }\n\n /**\n * A MathQuill-inner-field-like handle for a prompt.\n *\n * The old API handed callers a MathQuill field object, and consumers commonly\n * called `field.latex()` on it. There is no equivalent object in MathLive -\n * prompts are addressed by id - so this adapter exposes the parts that were\n * actually used: `id` and a get/set `latex()`.\n */\n promptHandle(id) {\n return {\n id,\n // MathQuill's inner fields could be focused directly; keep that.\n focus: () => {\n if (this.mathField) {\n this.mathField.focus();\n this.focusPrompt(id);\n }\n },\n latex: (value) => {\n if (!this.mathField) {\n return '';\n }\n\n if (value === undefined) {\n return fromMathLive(this.mathField.getPromptValue(id));\n }\n\n this.mathField.setPromptValue(id, toMathLive(value), {});\n\n return value;\n },\n };\n }\n\n /** All prompt handles, the analogue of `mathField.innerFields`. */\n get innerFields() {\n if (!this.mathField) {\n return [];\n }\n\n return (this.mathField.getPrompts() || []).map((id) => this.promptHandle(id));\n }\n\n onPromptInput = () => {\n const { onSubFieldChange, getFieldName } = this.props;\n\n if (!this.mathField) {\n return;\n }\n\n const next = this.values();\n\n this.announceConversion(next);\n\n if (!onSubFieldChange) {\n this.promptValues = next;\n return;\n }\n\n const fields = this.innerFields;\n\n // Emit only the prompts whose value actually changed, mirroring the\n // per-field granularity of the old onSubFieldChange(name, latex).\n Object.keys(next).forEach((id) => {\n if (next[id] === this.promptValues[id]) {\n return;\n }\n\n const name = getFieldName(this.promptHandle(id), fields) || id;\n\n onSubFieldChange(name, next[id]);\n });\n\n this.promptValues = next;\n };\n\n onPromptFocus = () => {\n const { onSubFieldFocus, getFieldName } = this.props;\n\n if (!this.mathField || !onSubFieldFocus) {\n return;\n }\n\n try {\n const ids = this.mathField.getPrompts() || [];\n // The focused prompt is the one containing the current selection.\n const focused = ids.find((id) => {\n const range = this.mathField.getPromptRange(id);\n const selection = this.mathField.selection;\n\n if (!range || !selection || !selection.ranges || !selection.ranges.length) {\n return false;\n }\n\n const [start, end] = range;\n const [selStart] = selection.ranges[0];\n\n return selStart >= start && selStart <= end;\n });\n\n if (focused) {\n const handle = this.promptHandle(focused);\n const name = getFieldName(handle, this.innerFields) || focused;\n\n if (this.props.setInput) {\n this.props.setInput(handle);\n }\n\n onSubFieldFocus(name, handle);\n }\n } catch (e) {\n log('error resolving focused prompt: %s', e.message);\n }\n };\n\n /** The id of the prompt whose range contains `position`, if any. */\n promptContaining(position) {\n if (!this.mathField || typeof position !== 'number') {\n return undefined;\n }\n\n return (this.mathField.getPrompts() || []).find((id) => {\n const range = this.mathField.getPromptRange(id);\n\n return range && position >= range[0] && position <= range[1];\n });\n }\n\n /**\n * Put a blinking caret inside an answer block.\n *\n * MathLive only makes the caret visible while the field element carries the\n * `ML__focused` class, and it adds that class during a *render*, gated on\n * `isSelectionEditable && hasFocus()`. In a read-only field only prompt\n * interiors are editable, so the two things both have to be true:\n *\n * - the selection must sit inside a prompt, and\n * - a render must run afterwards.\n *\n * Assigning `selection` satisfies the first but not the second - it updates\n * the model without re-rendering, so the class stays off and the caret span\n * remains `visibility: hidden`. Executing any command does trigger the\n * render, and `scrollIntoView` is the one with no other effect: it keeps the\n * caret on screen, which the preceding `focus()` already does anyway.\n *\n * @param {string} [id] prompt to focus; defaults to the first one\n */\n focusPrompt(id) {\n if (!this.mathField) {\n return;\n }\n\n const ids = this.mathField.getPrompts() || [];\n const target = id && ids.indexOf(id) !== -1 ? id : ids[0];\n\n if (!target) {\n return;\n }\n\n const range = this.mathField.getPromptRange(target);\n\n if (!range) {\n return;\n }\n\n this.mathField.selection = { ranges: [[range[0], range[0]]] };\n\n if (typeof this.mathField.executeCommand === 'function') {\n this.mathField.executeCommand('scrollIntoView');\n }\n }\n\n focus() {\n if (!this.mathField) {\n return;\n }\n\n this.mathField.focus();\n\n // A bare focus() leaves the selection outside every prompt, which in a\n // read-only field is not editable - the block looked focused but had no\n // cursor. Clicking a prompt already works, because MathLive's own pointer\n // handling moves the selection and re-renders.\n if (!this.promptContaining(this.mathField.position)) {\n this.focusPrompt();\n }\n }\n\n blur() {\n this.mathField && this.mathField.blur();\n }\n\n render() {\n const { className, onFocus, onBlur } = this.props;\n const { markup } = this.state;\n\n if (this.mathField || this.isInteractive()) {\n return <Holder className={className} onFocus={onFocus} onBlur={onBlur} ref={this.holderRef} />;\n }\n\n return (\n <Holder\n className={className}\n onFocus={onFocus}\n onBlur={onBlur}\n ref={this.holderRef}\n // eslint-disable-next-line react/no-danger\n dangerouslySetInnerHTML={{ __html: markup }}\n />\n );\n }\n}\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.1.4",
6
+ "version": "1.1.5-beta.10",
7
7
  "description": "MathLive-based math input (drop-in alternative to @pie-lib/math-input)",
8
8
  "main": "lib/index.js",
9
9
  "module": "src/index.jsx",
@@ -14,7 +14,7 @@
14
14
  "@emotion/styled": "^11.14.1",
15
15
  "@mui/icons-material": "^7.3.4",
16
16
  "@mui/material": "^7.3.4",
17
- "@pie-lib/render-ui": "^7.0.3",
17
+ "@pie-lib/render-ui": "^6.1.0",
18
18
  "debug": "^4.1.1",
19
19
  "lodash-es": "^4.17.23",
20
20
  "mathlive": "^0.110.0",
@@ -27,6 +27,5 @@
27
27
  "react": "^18.2.0",
28
28
  "react-dom": "^18.2.0"
29
29
  },
30
- "scripts": {},
31
- "gitHead": "555178e597d60d604dcc8b77cbb86dfde7b86b4f"
30
+ "scripts": {}
32
31
  }
@@ -0,0 +1,220 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+
4
+ import MathInput from '../math-input';
5
+ import HorizontalKeypad from '../horizontal-keypad';
6
+ import Input from '../mf/input';
7
+
8
+ /**
9
+ * The pieces that sit between the keypad and the field: how a pressed key is
10
+ * routed into the field, and the legacy `{value,type}` shape math-toolbar
11
+ * consumes.
12
+ */
13
+ describe('MathInput.keypadPress routing', () => {
14
+ const withInput = () => {
15
+ const mi = new MathInput({});
16
+ const calls = { write: [], keystroke: [] };
17
+
18
+ mi.input = {
19
+ write: (v) => calls.write.push(v),
20
+ keystroke: (v) => calls.keystroke.push(v),
21
+ };
22
+
23
+ return { mi, calls };
24
+ };
25
+
26
+ it('routes an insert action to write()', () => {
27
+ const { mi, calls } = withInput();
28
+
29
+ mi.keypadPress({ latex: '\\pi' });
30
+
31
+ expect(calls.write).toEqual(['\\pi']);
32
+ expect(calls.keystroke).toEqual([]);
33
+ });
34
+
35
+ it('expands an argument-taking command before writing', () => {
36
+ const { mi, calls } = withInput();
37
+
38
+ mi.keypadPress({ command: '\\frac' });
39
+
40
+ expect(calls.write).toEqual(['\\frac{#?}{#?}']);
41
+ });
42
+
43
+ it('routes a cursor keystroke to keystroke()', () => {
44
+ const { mi, calls } = withInput();
45
+
46
+ mi.keypadPress({ keystroke: 'Left' });
47
+
48
+ expect(calls.keystroke).toEqual(['moveToPreviousChar']);
49
+ expect(calls.write).toEqual([]);
50
+ });
51
+
52
+ it('ignores keys with no action', () => {
53
+ const { mi, calls } = withInput();
54
+
55
+ mi.keypadPress({});
56
+ mi.keypadPress({ keystroke: 'Nonsense' });
57
+
58
+ expect(calls.write).toEqual([]);
59
+ expect(calls.keystroke).toEqual([]);
60
+ });
61
+
62
+ it('is a no-op before the field ref exists', () => {
63
+ const mi = new MathInput({});
64
+
65
+ expect(() => mi.keypadPress({ latex: '\\pi' })).not.toThrow();
66
+ });
67
+
68
+ it('changeLatex only fires onChange when the value differs', () => {
69
+ const onChange = jest.fn();
70
+ const mi = new MathInput({ latex: 'x', onChange });
71
+
72
+ mi.changeLatex('x');
73
+ expect(onChange).not.toHaveBeenCalled();
74
+
75
+ mi.changeLatex('y');
76
+ expect(onChange).toHaveBeenCalledWith('y');
77
+ });
78
+
79
+ it('tracks focus state', () => {
80
+ const mi = new MathInput({});
81
+
82
+ mi.setState = (s) => Object.assign(mi.state, s);
83
+ mi.inputFocus();
84
+ expect(mi.state.focused).toBe(true);
85
+
86
+ mi.inputBlur();
87
+ expect(mi.state.focused).toBe(false);
88
+ });
89
+ });
90
+
91
+ describe('HorizontalKeypad legacy {value,type} model', () => {
92
+ const pressed = (key) => {
93
+ const onClick = jest.fn();
94
+ const hk = new HorizontalKeypad({ onClick });
95
+
96
+ hk.keypadPress(key);
97
+
98
+ return onClick.mock.calls[0][0];
99
+ };
100
+
101
+ it('maps a command to {value, type: command}', () => {
102
+ expect(pressed({ command: '\\frac' })).toEqual({ value: '\\frac', type: 'command' });
103
+ });
104
+
105
+ it('maps a write to {value}', () => {
106
+ expect(pressed({ write: 'x' })).toEqual({ value: 'x' });
107
+ });
108
+
109
+ it('maps a keystroke to {type: cursor}', () => {
110
+ expect(pressed({ keystroke: 'Left' })).toEqual({ type: 'cursor', value: 'Left' });
111
+ });
112
+
113
+ it('renders without throwing', () => {
114
+ expect(() => render(<HorizontalKeypad onClick={() => {}} />)).not.toThrow();
115
+ });
116
+ });
117
+
118
+ describe('Input: controlled-value sync and events', () => {
119
+ const withField = () => {
120
+ const input = new Input({});
121
+
122
+ input.mathField = {
123
+ value: '',
124
+ getValue: () => input.mathField.value,
125
+ insert(v) {
126
+ input.mathField.value += v;
127
+ },
128
+ executeCommand() {},
129
+ focus() {},
130
+ blur() {},
131
+ addEventListener() {},
132
+ removeEventListener() {},
133
+ remove() {},
134
+ };
135
+
136
+ return input;
137
+ };
138
+
139
+ it('emits stored-form latex on edit', () => {
140
+ const onChange = jest.fn();
141
+ const input = withField();
142
+
143
+ input.props = { onChange };
144
+ input.mathField.value = '\\longdiv{\\placeholder{}}';
145
+ input.onInputEdit();
146
+
147
+ // \placeholder is MathLive-internal and must not reach the model
148
+ expect(onChange).toHaveBeenCalledWith('\\longdiv{}');
149
+ expect(input.lastEmitted).toEqual('\\longdiv{}');
150
+ });
151
+
152
+ it('does not push a prop echo back into the field', () => {
153
+ const input = withField();
154
+
155
+ input.mathField.value = 'x';
156
+ input.lastEmitted = 'x';
157
+ input.props = { latex: 'x' };
158
+
159
+ input.componentDidUpdate({ latex: '' });
160
+
161
+ expect(input.mathField.value).toEqual('x');
162
+ });
163
+
164
+ it('pushes a genuinely new latex prop into the field', () => {
165
+ const input = withField();
166
+
167
+ input.mathField.value = 'x';
168
+ input.lastEmitted = 'x';
169
+ input.props = { latex: 'y' };
170
+
171
+ input.componentDidUpdate({ latex: 'x' });
172
+
173
+ expect(input.mathField.value).toEqual('y');
174
+ });
175
+
176
+ it('bridges stored latex on the way in', () => {
177
+ const input = withField();
178
+
179
+ input.props = { latex: '\\longdiv{5}' };
180
+ input.componentDidUpdate({ latex: '' });
181
+
182
+ // must become native \enclose so the content stays editable
183
+ expect(input.mathField.value).toEqual('\\enclose{longdiv}{5}');
184
+ });
185
+
186
+ it('toggles readOnly', () => {
187
+ const input = withField();
188
+
189
+ input.props = { readOnly: true };
190
+ input.componentDidUpdate({ readOnly: false });
191
+
192
+ expect(input.mathField.readOnly).toBe(true);
193
+ });
194
+
195
+ it('clear() empties the field', () => {
196
+ const input = withField();
197
+
198
+ input.mathField.value = 'abc';
199
+ expect(input.clear()).toEqual('');
200
+ expect(input.mathField.value).toEqual('');
201
+ });
202
+
203
+ it('suppresses Enter so it cannot insert a line break', () => {
204
+ const input = withField();
205
+ const preventDefault = jest.fn();
206
+
207
+ input.onKeyDown({ key: 'Enter', preventDefault });
208
+ expect(preventDefault).toHaveBeenCalled();
209
+
210
+ preventDefault.mockClear();
211
+ input.onKeyDown({ key: 'a', preventDefault });
212
+ expect(preventDefault).not.toHaveBeenCalled();
213
+ });
214
+
215
+ it('componentDidUpdate is safe before the field exists', () => {
216
+ const input = new Input({ latex: 'x' });
217
+
218
+ expect(() => input.componentDidUpdate({ latex: '' })).not.toThrow();
219
+ });
220
+ });
@@ -1,5 +1,5 @@
1
1
  import { withVisibleEmptySlots as latexForDisplay } from '../latex-bridge';
2
- import { keyToAction } from '../latex-bridge';
2
+ import { keyToAction, toMathLive } from '../latex-bridge';
3
3
 
4
4
  /**
5
5
  * Keys use empty groups to show a command's shape (`\frac{}{}`, `x^{}`).
@@ -11,10 +11,46 @@ describe('keypad label display normalisation', () => {
11
11
  const out = latexForDisplay('\\frac{}{}');
12
12
 
13
13
  expect(out).not.toEqual('\\frac{}{}');
14
- expect(out).toContain('25AB'); // white small square
14
+ // a \rule, not a glyph: font metrics for a substituted glyph do not match
15
+ // MathLive's, which left stretchy accents narrower than the box
16
+ expect(out).toContain('\\rule');
15
17
  expect(out).toContain('pie-empty');
16
18
  });
17
19
 
20
+ it.each([
21
+ ['absolute value', '\\abs{}', '|', '|'],
22
+ ['parenthesis', '\\left(\\right)', '(', ')'],
23
+ ['brackets', '\\left[\\right]', '[', ']'],
24
+ ])('gives an empty %s a tall, filled slot (the same muted tag as other slots)', (_name, latex, open, close) => {
25
+ const out = latexForDisplay(toMathLive(latex));
26
+
27
+ expect(out).toContain('pie-empty=1');
28
+ expect(out).toContain('\\rule[-0.25em]{0.55em}{1em}');
29
+ expect(out).not.toContain('\\boxed');
30
+ // the strut that keeps the fences tall while the box is centred on the axis
31
+ expect(out).toContain('\\rule{0em}{1em}');
32
+ expect(out.startsWith(`\\left${open}`)).toBe(true);
33
+ expect(out.endsWith(`\\right${close}`)).toBe(true);
34
+ });
35
+
36
+ it('aligns an empty segment with the ray and line keys: bar up to the shaft, box down', () => {
37
+ const out = latexForDisplay('\\overline{}');
38
+
39
+ // strut inside the bar (bar up) and one before it (label down)
40
+ expect(out).toMatch(/^\\rule\{0em\}\{1\.3em\}\\overline\{\\rule\{0em\}\{0\.87em\}/);
41
+ expect(out).toContain('pie-empty');
42
+ // the ray and line keys are the reference, so they are left as they were
43
+ expect(latexForDisplay('\\overrightarrow{}')).not.toContain('\\rule{0em}');
44
+ expect(latexForDisplay('\\overleftrightarrow{}')).not.toContain('\\rule{0em}');
45
+ // a filled bar is untouched
46
+ expect(latexForDisplay('\\overline{AB}')).toEqual('\\overline{AB}');
47
+ });
48
+
49
+ it('leaves a filled pair of fences alone', () => {
50
+ expect(latexForDisplay(toMathLive('\\abs{x}'))).toEqual('\\left|x\\right|');
51
+ expect(latexForDisplay('\\left(x\\right)')).toEqual('\\left(x\\right)');
52
+ });
53
+
18
54
  it('handles every empty-group key shape', () => {
19
55
  [
20
56
  '\\frac{}{}',
@@ -36,6 +72,197 @@ describe('keypad label display normalisation', () => {
36
72
  expect(latexForDisplay('\\frac{x}{ }')).toContain('pie-empty');
37
73
  });
38
74
 
75
+ // MathQuill needed a nested \overline{} to draw the empty box. MathLive
76
+ // renders that as a real line, so those labels showed a spurious bar (a line
77
+ // under the arrows, and a straight line instead of the arc).
78
+ it('accent keys do not carry the nested-overline box hack', () => {
79
+ // eslint-disable-next-line global-require
80
+ const geometry = require('../keys/geometry');
81
+
82
+ Object.values(geometry).forEach((key) => {
83
+ if (key && typeof key.latex === 'string') {
84
+ expect(key.latex).not.toContain('\\overline{}}');
85
+ }
86
+ });
87
+ });
88
+
89
+ it('an accent over an empty slot still gets a visible box', () => {
90
+ ['\\overleftrightarrow{}', '\\overarc{}'].forEach((latex) => {
91
+ const out = latexForDisplay(latex);
92
+
93
+ expect(out).toContain('pie-empty');
94
+ expect(out).not.toContain('\\overline');
95
+ });
96
+ });
97
+
98
+ // A glyph's width comes from font metrics; U+25AB is not in the KaTeX fonts,
99
+ // so the browser substituted a wider one and stretchy accents came out
100
+ // narrower than the box. A rule has an explicit width.
101
+ it('the empty slot has an explicit width, not font-dependent metrics', () => {
102
+ const out = latexForDisplay('\\overarc{}');
103
+
104
+ expect(out).toContain('\\rule');
105
+ expect(out).toMatch(/rule\{[\d.]+em\}\{[\d.]+em\}/);
106
+ expect(out).not.toContain('unicode');
107
+ });
108
+
109
+ // Measured in the browser: the accent's immediate wrapper computes to 0px
110
+ // (its .ML__stretchy child is absolutely positioned), so the svg width has to
111
+ // resolve against .ML__base - whose width equals the content. Without pinning
112
+ // that, the arc collapsed to a spike and sat right of the box.
113
+ it('pins the accent containing block and drops the centering shift', () => {
114
+ // eslint-disable-next-line global-require
115
+ const { placeholderStyles } = require('../mf/common-styles');
116
+
117
+ expect(placeholderStyles['& .ML__base'].position).toEqual('relative');
118
+ expect(placeholderStyles['& .ML__stretchy']).toEqual({ position: 'absolute', left: 0, width: '100%' });
119
+ // must be !important: MathLive writes margin-left as an inline style, and a
120
+ // class cannot override that. Leaving it un-important also halved the arc's
121
+ // width, because .ML__center is the stretchy's containing block.
122
+ expect(placeholderStyles['& .ML__center:has(.ML__stretchy)'].marginLeft).toEqual('0 !important');
123
+ });
124
+
125
+ // MathLive sets the index of \sqrt[n]{x} in scriptscriptstyle - an inline
126
+ // `font-size: 50%` - which is too small to read on the nth-root key and in
127
+ // authored radicals. It is scaled up to 80%.
128
+ describe('radical index size', () => {
129
+ // Real MathLive 0.110 output for `\sqrt[1]{2}`, trimmed to the index. The
130
+ // selector walks this exact chain, so this fixture is what pins it down.
131
+ const INDEX_MARKUP = `
132
+ <span class="ML__base">
133
+ <span class="ML__sqrt-index">
134
+ <span class="ML__vlist-t"><span class="ML__vlist-r"><span class="ML__vlist" style="height:0.78em">
135
+ <span style="top:-3.45em">
136
+ <span class="ML__pstrut" style="height:3em"></span>
137
+ <span id="index-size" style="height:0.33em;display:inline-block;font-size: 50%">
138
+ <span class="ML__cmr">1</span>
139
+ <span id="nested-size" style="font-size: 50%">nested</span>
140
+ </span>
141
+ </span>
142
+ </span></span></span>
143
+ </span>
144
+ <span class="ML__sqrt-sign"><span class="ML__delim-size1">&#8730;</span></span>
145
+ </span>`;
146
+
147
+ const rule = () => {
148
+ // eslint-disable-next-line global-require
149
+ const { rootIndexStyles } = require('../mf/common-styles');
150
+ const selectors = Object.keys(rootIndexStyles);
151
+
152
+ expect(selectors).toHaveLength(1);
153
+
154
+ return { selector: selectors[0], value: rootIndexStyles[selectors[0]].fontSize };
155
+ };
156
+
157
+ it("scales the index glyph to 80%, up from MathLive's 50%", () => {
158
+ // !important: the 50% is an inline style, which a class cannot override.
159
+ expect(rule().value).toEqual('80% !important');
160
+ });
161
+
162
+ it('targets the span MathLive puts the inline font-size on', () => {
163
+ const holder = document.createElement('div');
164
+
165
+ holder.innerHTML = INDEX_MARKUP;
166
+
167
+ const matched = holder.querySelectorAll(rule().selector.replace(/^&\s*/, ''));
168
+
169
+ expect(Array.from(matched).map((el) => el.id)).toEqual(['index-size']);
170
+ });
171
+
172
+ it('leaves the radical index box itself unscaled', () => {
173
+ // Scaling `.ML__sqrt-index` would scale the em-based top/height offsets
174
+ // that tuck the index into the radical, lifting it clear of the crook.
175
+ const { selector } = rule();
176
+
177
+ expect(selector).not.toMatch(/\.ML__sqrt-index\s*\{?$/);
178
+ expect(selector.startsWith('& .ML__sqrt-index >')).toBe(true);
179
+ });
180
+
181
+ it('applies wherever math renders - keypad labels, static math, mathfield', () => {
182
+ // eslint-disable-next-line global-require
183
+ const cs = require('../mf/common-styles');
184
+ const { selector } = rule();
185
+
186
+ ['commonKeyboardStyles', 'commonMathLiveStyles', 'supsubStyles'].forEach((name) => {
187
+ expect(cs[name][selector]).toBeDefined();
188
+ });
189
+
190
+ // The live mathfield renders in a shadow root, which host styles cannot
191
+ // reach, so the same rule ships as plain CSS for `applyShadowStyles`.
192
+ expect(cs.ROOT_INDEX_CSS).toContain('.ML__sqrt-index >');
193
+ // Derived, so the size lives in one place: the two copies cannot drift.
194
+ expect(cs.ROOT_INDEX_CSS).toContain(`font-size:${rule().value}`);
195
+ });
196
+ });
197
+
198
+ it('applies those rules wherever static math renders', () => {
199
+ // eslint-disable-next-line global-require
200
+ const cs = require('../mf/common-styles');
201
+
202
+ ['commonKeyboardStyles', 'commonMathLiveStyles'].forEach((name) => {
203
+ expect(cs[name]['& .ML__stretchy']).toBeDefined();
204
+ expect(cs[name]['& .ML__base']).toBeDefined();
205
+ });
206
+ });
207
+
208
+ // A bare `& svg` rule re-parents every svg in the subtree; MathLive's own rule
209
+ // is descendant-scoped to .ML__latex .ML__stretchy svg.
210
+ it('does not use a blanket svg rule', () => {
211
+ // eslint-disable-next-line global-require
212
+ const cs = require('../mf/common-styles');
213
+
214
+ Object.values(cs).forEach((style) => {
215
+ if (style && typeof style === 'object') {
216
+ expect(Object.keys(style)).not.toContain('& svg');
217
+ }
218
+ });
219
+ });
220
+
221
+ // The Percent key showed as a blank button on the Advanced Algebra keypad
222
+ // (row 1, position 6). MathQuill defined `LatexCmds['%']` as a symbol that
223
+ // serialised to `\\%`, so its bare `%` label parsed fine; MathLive parses real
224
+ // LaTeX, where `%` opens a comment - the label rendered nothing, and pressing
225
+ // the key inserted a comment that swallowed the rest of the expression.
226
+ it('the percent key is escaped, not a latex comment', () => {
227
+ // eslint-disable-next-line global-require
228
+ const { percentage } = require('../keys/misc');
229
+
230
+ expect(percentage.latex).toEqual('\\%');
231
+ expect(keyToAction(percentage).value).toEqual('\\%');
232
+ });
233
+
234
+ // Any bare `%` in a key would fail the same way, silently.
235
+ it('no keypad key carries an unescaped %', () => {
236
+ // eslint-disable-next-line global-require
237
+ const { baseSet } = require('../keys');
238
+ // eslint-disable-next-line global-require
239
+ const { gradeSets } = require('../keys/grades');
240
+
241
+ const every = [...baseSet, ...gradeSets.map((g) => g.set)];
242
+
243
+ const walk = (node) => {
244
+ if (Array.isArray(node)) {
245
+ node.forEach(walk);
246
+ return;
247
+ }
248
+
249
+ if (!node) {
250
+ return;
251
+ }
252
+
253
+ ['latex', 'write', 'command'].forEach((field) => {
254
+ const value = node[field];
255
+
256
+ if (typeof value === 'string') {
257
+ // a `%` is only safe when preceded by an odd run of backslashes
258
+ expect(value.replace(/\\./g, '')).not.toContain('%');
259
+ }
260
+ });
261
+ };
262
+
263
+ walk(every);
264
+ });
265
+
39
266
  it('leaves latex without empty groups alone', () => {
40
267
  ['\\pi', '\\sin', 'x^2', '\\overline{AB}'].forEach((latex) => {
41
268
  expect(latexForDisplay(latex)).toEqual(latex);