@pie-lib/math-input-mathlive 1.1.5-beta.2 → 1.1.5-beta.3

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.
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.withVisibleEmptySlots = exports.toNativeCommands = exports.toMathLive = exports.keyToAction = exports.fromMathLive = exports.fieldIds = exports.NEWLINE_EMBED = exports.KEYSTROKES = exports.DEFAULT_FIELD_ID = exports.COMMANDS_WITH_ARGS = void 0;
6
+ exports.withVisibleEmptySlots = exports.toNativeCommands = exports.toMathLive = exports.toCanonicalCommands = exports.keyToAction = exports.fromMathLive = exports.fieldIds = exports.NEWLINE_EMBED = exports.MATHQUILL_ALIASES = exports.KEYSTROKES = exports.DEFAULT_FIELD_ID = exports.COMMANDS_WITH_ARGS = void 0;
7
7
  /**
8
8
  * LaTeX translation layer between the MathQuill-era content model and MathLive.
9
9
  *
@@ -228,6 +228,39 @@ var toMathLive = exports.toMathLive = function toMathLive(latex) {
228
228
  return out;
229
229
  };
230
230
 
231
+ /**
232
+ * Keypad command names that MathQuill treated as ALIASES of a different command.
233
+ *
234
+ * MathQuill registered `\\divide` as another name for `\\div` (and
235
+ * `\\perpendicular` for `\\perp`), so pressing the key stored the real command.
236
+ * MathLive only knows these names through `PIE_MACROS`, and serialises a macro
237
+ * under its own name - so `\\divide` reached storage verbatim, where MathJax
238
+ * (@pie-lib/math-rendering) has no such command and renders it as red text.
239
+ *
240
+ * Every other keypad command already matches what MathQuill stored (`\\degree`,
241
+ * `\\nsim`, `\\ncong`, `\\nparallel`, `\\square`, `\\napprox`,
242
+ * `\\parallelogram`), and MathJax understands those.
243
+ */
244
+ var MATHQUILL_ALIASES = exports.MATHQUILL_ALIASES = {
245
+ '\\divide': '\\div',
246
+ '\\perpendicular': '\\perp'
247
+ };
248
+
249
+ // Whole command names only: `\\divideontimes` is a real AMS symbol.
250
+ var MATHQUILL_ALIAS_REGEX = /\\(divide|perpendicular)(?![a-zA-Z])/g;
251
+
252
+ /**
253
+ * Replace MathQuill alias names with the command MathQuill actually stored.
254
+ *
255
+ * @param {string} latex
256
+ * @returns {string}
257
+ */
258
+ var toCanonicalCommands = exports.toCanonicalCommands = function toCanonicalCommands(latex) {
259
+ return typeof latex === 'string' ? latex.replace(MATHQUILL_ALIAS_REGEX, function (m) {
260
+ return MATHQUILL_ALIASES[m];
261
+ }) : latex;
262
+ };
263
+
231
264
  /**
232
265
  * Convert latex out of MathLive back into the stored form, so items authored
233
266
  * with the MathLive editor remain readable by @pie-lib/math-rendering and by
@@ -263,7 +296,10 @@ var fromMathLive = exports.fromMathLive = function fromMathLive(latex) {
263
296
  return content;
264
297
  });
265
298
  out = out.replace(BARE_PLACEHOLDER_NO_ARG_REGEX, '');
266
- return out;
299
+
300
+ // Store the command MathQuill stored, not the alias - also heals latex that was
301
+ // saved with `\\divide` before the keypad resolved it.
302
+ return toCanonicalCommands(out);
267
303
  };
268
304
 
269
305
  /**
@@ -341,7 +377,7 @@ var keyToAction = exports.keyToAction = function keyToAction(key) {
341
377
  // each part and concatenate.
342
378
  var parts = Array.isArray(key.command) ? key.command : [key.command];
343
379
  var value = parts.map(function (c) {
344
- return COMMANDS_WITH_ARGS[c] || c;
380
+ return COMMANDS_WITH_ARGS[c] || MATHQUILL_ALIASES[c] || c;
345
381
  }).join('');
346
382
  return {
347
383
  type: 'insert',
@@ -1 +1 @@
1
- {"version":3,"file":"latex-bridge.js","names":["NEWLINE_EMBED","exports","NEWLINE_EMBED_REGEX","DISPLAYLINES_REGEX","MQ_FIELD_REGEX","PLACEHOLDER_REGEX","BARE_PLACEHOLDER_REGEX","BARE_PLACEHOLDER_NO_ARG_REGEX","DEFAULT_FIELD_ID","EMPTY_SLOT","EMPTY_GROUP_REGEX","withVisibleEmptySlots","latex","replace","concat","toNativeCommands","out","replaceAbs","escapeBareComments","indexOf","backslashes","i","length","ch","idx","open","depth","close","inner","slice","fieldIds","match","lastIndex","exec","push","toMathLive","_m","id","content","split","join","fromMathLive","trim","map","s","COMMANDS_WITH_ARGS","_","KEYSTROKES","Left","Right","Backspace","Delete","Up","Down","keyToAction","key","undefined","command","type","value","write","parts","Array","isArray","c","keystroke","selector"],"sources":["../src/latex-bridge.js"],"sourcesContent":["/**\n * LaTeX translation layer between the MathQuill-era content model and MathLive.\n *\n * Two independent concerns live here:\n *\n * 1. Newlines. MathQuill had no line break, so pie-lib registered a custom\n * `newLine` embed that serialises to `\\embed{newLine}[]`. MathLive has no\n * embed system but supports real multiline environments, so we translate\n * `\\embed{newLine}[]` <-> `\\\\` inside `\\displaylines{}`.\n *\n * Authored content keeps its `\\embed{newLine}[]` on-disk form (that is what\n * @pie-lib/math-rendering already normalises for MathJax), so each renderer\n * normalises at its own boundary and stored items stay compatible.\n *\n * 2. Keypad key -> MathLive action. MathQuill keys carry `write`/`command`/\n * `keystroke` built for `mf.write()` / `mf.cmd()` / `mf.keystroke()`.\n * MathLive has `insert()` and `executeCommand()` instead.\n */\n\nexport const NEWLINE_EMBED = '\\\\embed{newLine}[]';\nconst NEWLINE_EMBED_REGEX = /\\\\embed\\{newLine\\}\\[\\]/g;\nconst DISPLAYLINES_REGEX = /^\\\\displaylines\\{([\\s\\S]*)\\}$/;\n\n/**\n * MathQuill's editable sub-field syntax, e.g. `\\MathQuillMathField[r1]{}`.\n * These are the answer blocks in templated math items; MathLive expresses the\n * same idea as `\\placeholder[id]{}` and reads them back via the prompts API.\n */\nconst MQ_FIELD_REGEX = /\\\\MathQuillMathField\\[([^\\]]*)\\]\\{([^{}]*)\\}/g;\nconst PLACEHOLDER_REGEX = /\\\\placeholder\\[([^\\]]*)\\]\\{([^{}]*)\\}/g;\n\n/**\n * Unnamed placeholders, which MathLive inserts on its own.\n *\n * Typing a command with empty arguments (`\\longdiv{}`, `\\frac{}{}`) makes\n * MathLive serialise the empty slots as `\\placeholder{}`. That token is\n * MathLive-specific: MathJax - which @pie-lib/math-rendering uses to render\n * prompts and previews - has no such command and renders it as red error text.\n * Stored latex must therefore keep plain empty groups.\n */\nconst BARE_PLACEHOLDER_REGEX = /\\\\placeholder\\{([^{}]*)\\}/g;\nconst BARE_PLACEHOLDER_NO_ARG_REGEX = /\\\\placeholder(?![[{a-zA-Z])/g;\n\n/** Default id used when stored latex has an unnamed field. */\nexport const DEFAULT_FIELD_ID = 'r1';\n\n/**\n * A visible stand-in for an empty argument slot, used for STATIC rendering only.\n *\n * Stored latex legitimately contains empty groups - `\\longdiv{}`, `\\frac{}{}`,\n * `x^{}` - to express a command's shape. MathQuill drew those as a shaded box\n * (`.mq-empty`); MathLive renders `{}` as nothing, so `convertLatexToMarkup`\n * output comes out completely blank (not even the surrounding bracket, in the\n * case of `\\enclose{longdiv}{}`).\n *\n * `\\placeholder{}` is not usable here: in static markup it renders as a\n * non-breaking space, because its editable box only exists inside a live\n * mathfield. So something with real width is required.\n *\n * It must be a `\\rule`, not a unicode glyph. A glyph's width comes from font\n * metrics, and U+25AB is not in the KaTeX fonts - the browser substitutes a\n * wider fallback while MathLive still sizes surrounding accents from its own\n * metrics. That left stretchy accents (`\\overarc`, `\\overleftrightarrow`)\n * visibly narrower than the box they were meant to span. A rule carries an\n * explicit em width, so accents size correctly - and a filled box is closer to\n * MathQuill's `.mq-empty` anyway.\n *\n * Live mathfields need none of this - MathLive manages empty slots itself.\n */\nconst EMPTY_SLOT = '\\\\htmlData{pie-empty=1}{\\\\rule{0.55em}{0.55em}}';\nconst EMPTY_GROUP_REGEX = /\\{\\s*\\}/g;\n\n/**\n * Make empty argument slots visible, for static rendering.\n *\n * Display-only: never feed the result to a mathfield or to storage - the\n * original latex is what gets typed and saved.\n *\n * @param {string} latex\n * @returns {string}\n */\nexport const withVisibleEmptySlots = (latex) => (latex || '').replace(EMPTY_GROUP_REGEX, `{${EMPTY_SLOT}}`);\n\n/**\n * Pie commands that take an argument, and their native MathLive equivalents.\n *\n * These MUST NOT be handled as MathLive macros. A macro is serialised from the\n * arguments it was created with, so text typed inside its expansion never makes\n * it back out: `getValue('latex')` keeps returning `\\longdiv{\\placeholder{}}`\n * while `getValue('ascii-math')` shows the typed content. Setting\n * `captureSelection: false` lets the cursor in but does not change\n * serialisation.\n *\n * Expanding to native constructs instead gives real, editable atoms that\n * round-trip. All three targets are understood by MathJax too, so stored latex\n * still renders in @pie-lib/math-rendering without a reverse mapping.\n */\n/**\n * Rewrite argument-taking pie commands into native MathLive latex.\n * Applied on the way *into* a mathfield.\n *\n * @param {string} latex\n * @returns {string}\n */\nexport const toNativeCommands = (latex) => {\n if (typeof latex !== 'string' || !latex) {\n return latex || '';\n }\n\n let out = latex;\n\n // \\abs{x} -> \\left|x\\right| needs brace-aware handling, the rest are prefix\n // swaps.\n out = replaceAbs(out);\n out = out.replace(/\\\\longdiv\\{/g, '\\\\enclose{longdiv}{');\n out = out.replace(/\\\\overarc\\{/g, '\\\\overparen{');\n out = escapeBareComments(out);\n\n return out;\n};\n\n/**\n * Escape an unescaped `%`.\n *\n * In real LaTeX `%` starts a comment, so MathLive discards it *and everything\n * after it*: `50%` renders as `50`, and `x%y` as just `x`. MathQuill had no\n * such notion - it treated `%` as a symbol - so latex from anywhere outside the\n * keypad may still carry a bare one.\n *\n * Only a `%` preceded by an even number of backslashes is bare: `\\%` is already\n * escaped, while the `%` in `\\\\%` (a line break, then a comment) is not.\n *\n * @param {string} latex\n * @returns {string}\n */\nconst escapeBareComments = (latex) => {\n if (latex.indexOf('%') === -1) {\n return latex;\n }\n\n let out = '';\n // Consecutive backslashes immediately before the current character.\n let backslashes = 0;\n\n for (let i = 0; i < latex.length; i++) {\n const ch = latex[i];\n\n out += ch === '%' && backslashes % 2 === 0 ? '\\\\%' : ch;\n backslashes = ch === '\\\\' ? backslashes + 1 : 0;\n }\n\n return out;\n};\n\n/** `\\abs{x}` -> `\\left|x\\right|`, matching balanced braces. */\nconst replaceAbs = (latex) => {\n let out = latex;\n let idx = out.indexOf('\\\\abs{');\n\n while (idx !== -1) {\n const open = idx + '\\\\abs{'.length - 1;\n let depth = 0;\n let close = -1;\n\n for (let i = open; i < out.length; i++) {\n if (out[i] === '{') depth++;\n else if (out[i] === '}') {\n depth--;\n if (depth === 0) {\n close = i;\n break;\n }\n }\n }\n\n if (close === -1) {\n break;\n }\n\n const inner = out.slice(open + 1, close);\n\n out = `${out.slice(0, idx)}\\\\left|${inner}\\\\right|${out.slice(close + 1)}`;\n idx = out.indexOf('\\\\abs{');\n }\n\n return out;\n};\n\n/**\n * Ids of the answer blocks in a piece of stored latex, in document order.\n *\n * @param {string} latex\n * @returns {string[]}\n */\nexport const fieldIds = (latex) => {\n if (typeof latex !== 'string') {\n return [];\n }\n\n const out = [];\n let match;\n\n MQ_FIELD_REGEX.lastIndex = 0;\n\n while ((match = MQ_FIELD_REGEX.exec(latex)) !== null) {\n out.push(match[1] || DEFAULT_FIELD_ID);\n }\n\n return out;\n};\n\n/**\n * Convert stored latex (which may contain `\\embed{newLine}[]`) into latex that\n * MathLive can parse and render as multiple lines.\n *\n * @param {string} latex\n * @returns {string}\n */\nexport const toMathLive = (latex) => {\n if (typeof latex !== 'string' || !latex) {\n return latex || '';\n }\n\n // Argument-taking pie commands -> native constructs, so their content stays\n // editable and serialises back correctly (see toNativeCommands).\n let out = toNativeCommands(latex);\n\n // Answer blocks: \\MathQuillMathField[id]{x} -> \\placeholder[id]{x}\n out = out.replace(MQ_FIELD_REGEX, (_m, id, content) => `\\\\placeholder[${id || DEFAULT_FIELD_ID}]{${content}}`);\n\n // Newlines: \\embed{newLine}[] -> a multiline environment\n if (out.indexOf(NEWLINE_EMBED) !== -1) {\n out = `\\\\displaylines{${out.split(NEWLINE_EMBED_REGEX).join(' \\\\\\\\ ')}}`;\n }\n\n return out;\n};\n\n/**\n * Convert latex out of MathLive back into the stored form, so items authored\n * with the MathLive editor remain readable by @pie-lib/math-rendering and by\n * the existing MathQuill implementation.\n *\n * @param {string} latex\n * @returns {string}\n */\nexport const fromMathLive = (latex) => {\n if (typeof latex !== 'string' || !latex) {\n return latex || '';\n }\n\n const match = latex.trim().match(DISPLAYLINES_REGEX);\n let out = match ? match[1] : latex;\n\n // `\\\\` is the line separator inside a multiline environment.\n if (out.indexOf('\\\\\\\\') !== -1) {\n out = out\n .split(/\\s*\\\\\\\\\\s*/)\n .map((s) => s.trim())\n .join(NEWLINE_EMBED);\n }\n\n // Answer blocks: \\placeholder[id]{x} -> \\MathQuillMathField[id]{x}\n out = out.replace(\n PLACEHOLDER_REGEX,\n (_m, id, content) => `\\\\MathQuillMathField[${id || DEFAULT_FIELD_ID}]{${content}}`,\n );\n\n // Unnamed placeholders are MathLive's own representation of an empty slot and\n // must not reach storage or MathJax. Unwrap them: keep any content, drop the\n // command itself (the surrounding braces are already part of the host latex,\n // so re-adding them here would produce `\\longdiv{{}}`).\n out = out.replace(BARE_PLACEHOLDER_REGEX, (_m, content) => content);\n out = out.replace(BARE_PLACEHOLDER_NO_ARG_REGEX, '');\n\n return out;\n};\n\n/**\n * MathQuill `cmd()` semantics: commands that take arguments should land the\n * cursor inside the first argument. MathLive expresses that with `#?`\n * placeholders in the inserted latex.\n */\nconst COMMANDS_WITH_ARGS = {\n // An explicit \"blank over blank\" key: both slots start empty.\n '\\\\frac': '\\\\frac{#?}{#?}',\n // Division, which must behave like MathQuill's `/`: whatever precedes the\n // cursor becomes the numerator and the caret lands in the denominator. `#@`\n // is MathLive's token for \"the selection, or the item before the cursor\" -\n // this is exactly the template MathLive binds to the `/` key itself. Using\n // `#?` here instead produced an empty fraction and left the typed number\n // stranded outside it.\n '/': '\\\\frac{#@}{#?}',\n '\\\\sqrt': '\\\\sqrt{#?}',\n '\\\\nthroot': '\\\\sqrt[#?]{#?}',\n '\\\\overline': '\\\\overline{#?}',\n '\\\\overrightarrow': '\\\\overrightarrow{#?}',\n '\\\\overleftrightarrow': '\\\\overleftrightarrow{#?}',\n // Native forms, not the pie macros: a macro would not serialise the user's\n // typed content back out (see toNativeCommands).\n '\\\\overarc': '\\\\overparen{#?}',\n '\\\\longdiv': '\\\\enclose{longdiv}{#?}',\n '^': '^{#?}',\n _: '_{#?}',\n '(': '\\\\left(#?\\\\right)',\n '[': '\\\\left[#?\\\\right]',\n '|': '\\\\left|#?\\\\right|',\n};\n\n/**\n * MathQuill keystroke names -> MathLive command selectors.\n */\nconst KEYSTROKES = {\n Left: 'moveToPreviousChar',\n Right: 'moveToNextChar',\n Backspace: 'deleteBackward',\n Delete: 'deleteForward',\n Up: 'moveUp',\n Down: 'moveDown',\n};\n\n/**\n * Translate a keypad key definition into a MathLive action.\n *\n * Mirrors the precedence in @pie-lib/math-input's MathInput.keypadPress:\n * latex (unless a command is present) -> write -> command -> keystroke.\n *\n * @param {object} key\n * @returns {{type: 'insert'|'command', value: string}|undefined}\n */\nexport const keyToAction = (key) => {\n if (!key) {\n return undefined;\n }\n\n if (key.latex && !key.command) {\n return { type: 'insert', value: key.latex };\n }\n\n if (key.write) {\n return { type: 'insert', value: key.write };\n }\n\n if (key.command) {\n // Some keys carry an array of commands (e.g. Measured Angle is\n // ['m', '\\\\angle'], log-base-n is ['\\\\log', '_']). MathQuill's `cmd()`\n // applied them in sequence; MathLive inserts one latex string, so expand\n // each part and concatenate.\n const parts = Array.isArray(key.command) ? key.command : [key.command];\n const value = parts.map((c) => COMMANDS_WITH_ARGS[c] || c).join('');\n\n return { type: 'insert', value };\n }\n\n if (key.keystroke) {\n const selector = KEYSTROKES[key.keystroke];\n\n return selector ? { type: 'command', value: selector } : undefined;\n }\n\n return undefined;\n};\n\nexport { COMMANDS_WITH_ARGS, KEYSTROKES };\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"latex-bridge.js","names":["NEWLINE_EMBED","exports","NEWLINE_EMBED_REGEX","DISPLAYLINES_REGEX","MQ_FIELD_REGEX","PLACEHOLDER_REGEX","BARE_PLACEHOLDER_REGEX","BARE_PLACEHOLDER_NO_ARG_REGEX","DEFAULT_FIELD_ID","EMPTY_SLOT","EMPTY_GROUP_REGEX","withVisibleEmptySlots","latex","replace","concat","toNativeCommands","out","replaceAbs","escapeBareComments","indexOf","backslashes","i","length","ch","idx","open","depth","close","inner","slice","fieldIds","match","lastIndex","exec","push","toMathLive","_m","id","content","split","join","MATHQUILL_ALIASES","MATHQUILL_ALIAS_REGEX","toCanonicalCommands","m","fromMathLive","trim","map","s","COMMANDS_WITH_ARGS","_","KEYSTROKES","Left","Right","Backspace","Delete","Up","Down","keyToAction","key","undefined","command","type","value","write","parts","Array","isArray","c","keystroke","selector"],"sources":["../src/latex-bridge.js"],"sourcesContent":["/**\n * LaTeX translation layer between the MathQuill-era content model and MathLive.\n *\n * Two independent concerns live here:\n *\n * 1. Newlines. MathQuill had no line break, so pie-lib registered a custom\n * `newLine` embed that serialises to `\\embed{newLine}[]`. MathLive has no\n * embed system but supports real multiline environments, so we translate\n * `\\embed{newLine}[]` <-> `\\\\` inside `\\displaylines{}`.\n *\n * Authored content keeps its `\\embed{newLine}[]` on-disk form (that is what\n * @pie-lib/math-rendering already normalises for MathJax), so each renderer\n * normalises at its own boundary and stored items stay compatible.\n *\n * 2. Keypad key -> MathLive action. MathQuill keys carry `write`/`command`/\n * `keystroke` built for `mf.write()` / `mf.cmd()` / `mf.keystroke()`.\n * MathLive has `insert()` and `executeCommand()` instead.\n */\n\nexport const NEWLINE_EMBED = '\\\\embed{newLine}[]';\nconst NEWLINE_EMBED_REGEX = /\\\\embed\\{newLine\\}\\[\\]/g;\nconst DISPLAYLINES_REGEX = /^\\\\displaylines\\{([\\s\\S]*)\\}$/;\n\n/**\n * MathQuill's editable sub-field syntax, e.g. `\\MathQuillMathField[r1]{}`.\n * These are the answer blocks in templated math items; MathLive expresses the\n * same idea as `\\placeholder[id]{}` and reads them back via the prompts API.\n */\nconst MQ_FIELD_REGEX = /\\\\MathQuillMathField\\[([^\\]]*)\\]\\{([^{}]*)\\}/g;\nconst PLACEHOLDER_REGEX = /\\\\placeholder\\[([^\\]]*)\\]\\{([^{}]*)\\}/g;\n\n/**\n * Unnamed placeholders, which MathLive inserts on its own.\n *\n * Typing a command with empty arguments (`\\longdiv{}`, `\\frac{}{}`) makes\n * MathLive serialise the empty slots as `\\placeholder{}`. That token is\n * MathLive-specific: MathJax - which @pie-lib/math-rendering uses to render\n * prompts and previews - has no such command and renders it as red error text.\n * Stored latex must therefore keep plain empty groups.\n */\nconst BARE_PLACEHOLDER_REGEX = /\\\\placeholder\\{([^{}]*)\\}/g;\nconst BARE_PLACEHOLDER_NO_ARG_REGEX = /\\\\placeholder(?![[{a-zA-Z])/g;\n\n/** Default id used when stored latex has an unnamed field. */\nexport const DEFAULT_FIELD_ID = 'r1';\n\n/**\n * A visible stand-in for an empty argument slot, used for STATIC rendering only.\n *\n * Stored latex legitimately contains empty groups - `\\longdiv{}`, `\\frac{}{}`,\n * `x^{}` - to express a command's shape. MathQuill drew those as a shaded box\n * (`.mq-empty`); MathLive renders `{}` as nothing, so `convertLatexToMarkup`\n * output comes out completely blank (not even the surrounding bracket, in the\n * case of `\\enclose{longdiv}{}`).\n *\n * `\\placeholder{}` is not usable here: in static markup it renders as a\n * non-breaking space, because its editable box only exists inside a live\n * mathfield. So something with real width is required.\n *\n * It must be a `\\rule`, not a unicode glyph. A glyph's width comes from font\n * metrics, and U+25AB is not in the KaTeX fonts - the browser substitutes a\n * wider fallback while MathLive still sizes surrounding accents from its own\n * metrics. That left stretchy accents (`\\overarc`, `\\overleftrightarrow`)\n * visibly narrower than the box they were meant to span. A rule carries an\n * explicit em width, so accents size correctly - and a filled box is closer to\n * MathQuill's `.mq-empty` anyway.\n *\n * Live mathfields need none of this - MathLive manages empty slots itself.\n */\nconst EMPTY_SLOT = '\\\\htmlData{pie-empty=1}{\\\\rule{0.55em}{0.55em}}';\nconst EMPTY_GROUP_REGEX = /\\{\\s*\\}/g;\n\n/**\n * Make empty argument slots visible, for static rendering.\n *\n * Display-only: never feed the result to a mathfield or to storage - the\n * original latex is what gets typed and saved.\n *\n * @param {string} latex\n * @returns {string}\n */\nexport const withVisibleEmptySlots = (latex) => (latex || '').replace(EMPTY_GROUP_REGEX, `{${EMPTY_SLOT}}`);\n\n/**\n * Pie commands that take an argument, and their native MathLive equivalents.\n *\n * These MUST NOT be handled as MathLive macros. A macro is serialised from the\n * arguments it was created with, so text typed inside its expansion never makes\n * it back out: `getValue('latex')` keeps returning `\\longdiv{\\placeholder{}}`\n * while `getValue('ascii-math')` shows the typed content. Setting\n * `captureSelection: false` lets the cursor in but does not change\n * serialisation.\n *\n * Expanding to native constructs instead gives real, editable atoms that\n * round-trip. All three targets are understood by MathJax too, so stored latex\n * still renders in @pie-lib/math-rendering without a reverse mapping.\n */\n/**\n * Rewrite argument-taking pie commands into native MathLive latex.\n * Applied on the way *into* a mathfield.\n *\n * @param {string} latex\n * @returns {string}\n */\nexport const toNativeCommands = (latex) => {\n if (typeof latex !== 'string' || !latex) {\n return latex || '';\n }\n\n let out = latex;\n\n // \\abs{x} -> \\left|x\\right| needs brace-aware handling, the rest are prefix\n // swaps.\n out = replaceAbs(out);\n out = out.replace(/\\\\longdiv\\{/g, '\\\\enclose{longdiv}{');\n out = out.replace(/\\\\overarc\\{/g, '\\\\overparen{');\n out = escapeBareComments(out);\n\n return out;\n};\n\n/**\n * Escape an unescaped `%`.\n *\n * In real LaTeX `%` starts a comment, so MathLive discards it *and everything\n * after it*: `50%` renders as `50`, and `x%y` as just `x`. MathQuill had no\n * such notion - it treated `%` as a symbol - so latex from anywhere outside the\n * keypad may still carry a bare one.\n *\n * Only a `%` preceded by an even number of backslashes is bare: `\\%` is already\n * escaped, while the `%` in `\\\\%` (a line break, then a comment) is not.\n *\n * @param {string} latex\n * @returns {string}\n */\nconst escapeBareComments = (latex) => {\n if (latex.indexOf('%') === -1) {\n return latex;\n }\n\n let out = '';\n // Consecutive backslashes immediately before the current character.\n let backslashes = 0;\n\n for (let i = 0; i < latex.length; i++) {\n const ch = latex[i];\n\n out += ch === '%' && backslashes % 2 === 0 ? '\\\\%' : ch;\n backslashes = ch === '\\\\' ? backslashes + 1 : 0;\n }\n\n return out;\n};\n\n/** `\\abs{x}` -> `\\left|x\\right|`, matching balanced braces. */\nconst replaceAbs = (latex) => {\n let out = latex;\n let idx = out.indexOf('\\\\abs{');\n\n while (idx !== -1) {\n const open = idx + '\\\\abs{'.length - 1;\n let depth = 0;\n let close = -1;\n\n for (let i = open; i < out.length; i++) {\n if (out[i] === '{') depth++;\n else if (out[i] === '}') {\n depth--;\n if (depth === 0) {\n close = i;\n break;\n }\n }\n }\n\n if (close === -1) {\n break;\n }\n\n const inner = out.slice(open + 1, close);\n\n out = `${out.slice(0, idx)}\\\\left|${inner}\\\\right|${out.slice(close + 1)}`;\n idx = out.indexOf('\\\\abs{');\n }\n\n return out;\n};\n\n/**\n * Ids of the answer blocks in a piece of stored latex, in document order.\n *\n * @param {string} latex\n * @returns {string[]}\n */\nexport const fieldIds = (latex) => {\n if (typeof latex !== 'string') {\n return [];\n }\n\n const out = [];\n let match;\n\n MQ_FIELD_REGEX.lastIndex = 0;\n\n while ((match = MQ_FIELD_REGEX.exec(latex)) !== null) {\n out.push(match[1] || DEFAULT_FIELD_ID);\n }\n\n return out;\n};\n\n/**\n * Convert stored latex (which may contain `\\embed{newLine}[]`) into latex that\n * MathLive can parse and render as multiple lines.\n *\n * @param {string} latex\n * @returns {string}\n */\nexport const toMathLive = (latex) => {\n if (typeof latex !== 'string' || !latex) {\n return latex || '';\n }\n\n // Argument-taking pie commands -> native constructs, so their content stays\n // editable and serialises back correctly (see toNativeCommands).\n let out = toNativeCommands(latex);\n\n // Answer blocks: \\MathQuillMathField[id]{x} -> \\placeholder[id]{x}\n out = out.replace(MQ_FIELD_REGEX, (_m, id, content) => `\\\\placeholder[${id || DEFAULT_FIELD_ID}]{${content}}`);\n\n // Newlines: \\embed{newLine}[] -> a multiline environment\n if (out.indexOf(NEWLINE_EMBED) !== -1) {\n out = `\\\\displaylines{${out.split(NEWLINE_EMBED_REGEX).join(' \\\\\\\\ ')}}`;\n }\n\n return out;\n};\n\n/**\n * Keypad command names that MathQuill treated as ALIASES of a different command.\n *\n * MathQuill registered `\\\\divide` as another name for `\\\\div` (and\n * `\\\\perpendicular` for `\\\\perp`), so pressing the key stored the real command.\n * MathLive only knows these names through `PIE_MACROS`, and serialises a macro\n * under its own name - so `\\\\divide` reached storage verbatim, where MathJax\n * (@pie-lib/math-rendering) has no such command and renders it as red text.\n *\n * Every other keypad command already matches what MathQuill stored (`\\\\degree`,\n * `\\\\nsim`, `\\\\ncong`, `\\\\nparallel`, `\\\\square`, `\\\\napprox`,\n * `\\\\parallelogram`), and MathJax understands those.\n */\nconst MATHQUILL_ALIASES = {\n '\\\\divide': '\\\\div',\n '\\\\perpendicular': '\\\\perp',\n};\n\n// Whole command names only: `\\\\divideontimes` is a real AMS symbol.\nconst MATHQUILL_ALIAS_REGEX = /\\\\(divide|perpendicular)(?![a-zA-Z])/g;\n\n/**\n * Replace MathQuill alias names with the command MathQuill actually stored.\n *\n * @param {string} latex\n * @returns {string}\n */\nexport const toCanonicalCommands = (latex) =>\n typeof latex === 'string' ? latex.replace(MATHQUILL_ALIAS_REGEX, (m) => MATHQUILL_ALIASES[m]) : latex;\n\n/**\n * Convert latex out of MathLive back into the stored form, so items authored\n * with the MathLive editor remain readable by @pie-lib/math-rendering and by\n * the existing MathQuill implementation.\n *\n * @param {string} latex\n * @returns {string}\n */\nexport const fromMathLive = (latex) => {\n if (typeof latex !== 'string' || !latex) {\n return latex || '';\n }\n\n const match = latex.trim().match(DISPLAYLINES_REGEX);\n let out = match ? match[1] : latex;\n\n // `\\\\` is the line separator inside a multiline environment.\n if (out.indexOf('\\\\\\\\') !== -1) {\n out = out\n .split(/\\s*\\\\\\\\\\s*/)\n .map((s) => s.trim())\n .join(NEWLINE_EMBED);\n }\n\n // Answer blocks: \\placeholder[id]{x} -> \\MathQuillMathField[id]{x}\n out = out.replace(\n PLACEHOLDER_REGEX,\n (_m, id, content) => `\\\\MathQuillMathField[${id || DEFAULT_FIELD_ID}]{${content}}`,\n );\n\n // Unnamed placeholders are MathLive's own representation of an empty slot and\n // must not reach storage or MathJax. Unwrap them: keep any content, drop the\n // command itself (the surrounding braces are already part of the host latex,\n // so re-adding them here would produce `\\longdiv{{}}`).\n out = out.replace(BARE_PLACEHOLDER_REGEX, (_m, content) => content);\n out = out.replace(BARE_PLACEHOLDER_NO_ARG_REGEX, '');\n\n // Store the command MathQuill stored, not the alias - also heals latex that was\n // saved with `\\\\divide` before the keypad resolved it.\n return toCanonicalCommands(out);\n};\n\n/**\n * MathQuill `cmd()` semantics: commands that take arguments should land the\n * cursor inside the first argument. MathLive expresses that with `#?`\n * placeholders in the inserted latex.\n */\nconst COMMANDS_WITH_ARGS = {\n // An explicit \"blank over blank\" key: both slots start empty.\n '\\\\frac': '\\\\frac{#?}{#?}',\n // Division, which must behave like MathQuill's `/`: whatever precedes the\n // cursor becomes the numerator and the caret lands in the denominator. `#@`\n // is MathLive's token for \"the selection, or the item before the cursor\" -\n // this is exactly the template MathLive binds to the `/` key itself. Using\n // `#?` here instead produced an empty fraction and left the typed number\n // stranded outside it.\n '/': '\\\\frac{#@}{#?}',\n '\\\\sqrt': '\\\\sqrt{#?}',\n '\\\\nthroot': '\\\\sqrt[#?]{#?}',\n '\\\\overline': '\\\\overline{#?}',\n '\\\\overrightarrow': '\\\\overrightarrow{#?}',\n '\\\\overleftrightarrow': '\\\\overleftrightarrow{#?}',\n // Native forms, not the pie macros: a macro would not serialise the user's\n // typed content back out (see toNativeCommands).\n '\\\\overarc': '\\\\overparen{#?}',\n '\\\\longdiv': '\\\\enclose{longdiv}{#?}',\n '^': '^{#?}',\n _: '_{#?}',\n '(': '\\\\left(#?\\\\right)',\n '[': '\\\\left[#?\\\\right]',\n '|': '\\\\left|#?\\\\right|',\n};\n\n/**\n * MathQuill keystroke names -> MathLive command selectors.\n */\nconst KEYSTROKES = {\n Left: 'moveToPreviousChar',\n Right: 'moveToNextChar',\n Backspace: 'deleteBackward',\n Delete: 'deleteForward',\n Up: 'moveUp',\n Down: 'moveDown',\n};\n\n/**\n * Translate a keypad key definition into a MathLive action.\n *\n * Mirrors the precedence in @pie-lib/math-input's MathInput.keypadPress:\n * latex (unless a command is present) -> write -> command -> keystroke.\n *\n * @param {object} key\n * @returns {{type: 'insert'|'command', value: string}|undefined}\n */\nexport const keyToAction = (key) => {\n if (!key) {\n return undefined;\n }\n\n if (key.latex && !key.command) {\n return { type: 'insert', value: key.latex };\n }\n\n if (key.write) {\n return { type: 'insert', value: key.write };\n }\n\n if (key.command) {\n // Some keys carry an array of commands (e.g. Measured Angle is\n // ['m', '\\\\angle'], log-base-n is ['\\\\log', '_']). MathQuill's `cmd()`\n // applied them in sequence; MathLive inserts one latex string, so expand\n // each part and concatenate.\n const parts = Array.isArray(key.command) ? key.command : [key.command];\n const value = parts.map((c) => COMMANDS_WITH_ARGS[c] || MATHQUILL_ALIASES[c] || c).join('');\n\n return { type: 'insert', value };\n }\n\n if (key.keystroke) {\n const selector = KEYSTROKES[key.keystroke];\n\n return selector ? { type: 'command', value: selector } : undefined;\n }\n\n return undefined;\n};\n\nexport { COMMANDS_WITH_ARGS, KEYSTROKES, MATHQUILL_ALIASES };\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.5-beta.2",
6
+ "version": "1.1.5-beta.3",
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",
@@ -236,6 +236,36 @@ export const toMathLive = (latex) => {
236
236
  return out;
237
237
  };
238
238
 
239
+ /**
240
+ * Keypad command names that MathQuill treated as ALIASES of a different command.
241
+ *
242
+ * MathQuill registered `\\divide` as another name for `\\div` (and
243
+ * `\\perpendicular` for `\\perp`), so pressing the key stored the real command.
244
+ * MathLive only knows these names through `PIE_MACROS`, and serialises a macro
245
+ * under its own name - so `\\divide` reached storage verbatim, where MathJax
246
+ * (@pie-lib/math-rendering) has no such command and renders it as red text.
247
+ *
248
+ * Every other keypad command already matches what MathQuill stored (`\\degree`,
249
+ * `\\nsim`, `\\ncong`, `\\nparallel`, `\\square`, `\\napprox`,
250
+ * `\\parallelogram`), and MathJax understands those.
251
+ */
252
+ const MATHQUILL_ALIASES = {
253
+ '\\divide': '\\div',
254
+ '\\perpendicular': '\\perp',
255
+ };
256
+
257
+ // Whole command names only: `\\divideontimes` is a real AMS symbol.
258
+ const MATHQUILL_ALIAS_REGEX = /\\(divide|perpendicular)(?![a-zA-Z])/g;
259
+
260
+ /**
261
+ * Replace MathQuill alias names with the command MathQuill actually stored.
262
+ *
263
+ * @param {string} latex
264
+ * @returns {string}
265
+ */
266
+ export const toCanonicalCommands = (latex) =>
267
+ typeof latex === 'string' ? latex.replace(MATHQUILL_ALIAS_REGEX, (m) => MATHQUILL_ALIASES[m]) : latex;
268
+
239
269
  /**
240
270
  * Convert latex out of MathLive back into the stored form, so items authored
241
271
  * with the MathLive editor remain readable by @pie-lib/math-rendering and by
@@ -273,7 +303,9 @@ export const fromMathLive = (latex) => {
273
303
  out = out.replace(BARE_PLACEHOLDER_REGEX, (_m, content) => content);
274
304
  out = out.replace(BARE_PLACEHOLDER_NO_ARG_REGEX, '');
275
305
 
276
- return out;
306
+ // Store the command MathQuill stored, not the alias - also heals latex that was
307
+ // saved with `\\divide` before the keypad resolved it.
308
+ return toCanonicalCommands(out);
277
309
  };
278
310
 
279
311
  /**
@@ -347,7 +379,7 @@ export const keyToAction = (key) => {
347
379
  // applied them in sequence; MathLive inserts one latex string, so expand
348
380
  // each part and concatenate.
349
381
  const parts = Array.isArray(key.command) ? key.command : [key.command];
350
- const value = parts.map((c) => COMMANDS_WITH_ARGS[c] || c).join('');
382
+ const value = parts.map((c) => COMMANDS_WITH_ARGS[c] || MATHQUILL_ALIASES[c] || c).join('');
351
383
 
352
384
  return { type: 'insert', value };
353
385
  }
@@ -361,4 +393,4 @@ export const keyToAction = (key) => {
361
393
  return undefined;
362
394
  };
363
395
 
364
- export { COMMANDS_WITH_ARGS, KEYSTROKES };
396
+ export { COMMANDS_WITH_ARGS, KEYSTROKES, MATHQUILL_ALIASES };