diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index b40b5a65e..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,358 +0,0 @@ -{ - "env": { - "browser": true, - // "commonjs": true, - // "es6": true, - "es2020": true, - "jest": true, - "node": true, // For jest, config files, etc... - "amd": true, - "mocha": true - }, - - "parser": "@typescript-eslint/parser", - - "parserOptions": { - // Configure the parser with the tsconfig file in the root project - // (or rather an extended version that add peripheral files such as rollup.config.js - // so that eslint doesn't complain that they are not in the project) - "project": "./tsconfig.eslint.json", - - // "ecmaFeatures": { "jsx": false }, // default - "ecmaVersion": 2020, - // "sourceType": "module" // Allow the use of import - "extraFileExtensions": [".less"] - }, - - "extends": [ - // Uses the recommended rules for Typescript - "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended" - - // "plugin:no-unsanitized/DOM" - ], - - // Note: - // "off" or 0 - turn the rule off - // "warn" or 1 - turn the rule on as a warning (doesn’t affect exit code) - // "error" or 2 - turn the rule on as an error (exit code is 1 when triggered) - // See http://eslint.org/docs/rules/ - "rules": { - // "no-unsanitized/method": [ - // "warn", - // { - // // "disableDefault": true, - // "escape": { - // "methods": [ - // "createHTML", - // "mathfield.config.createHTML", - // "_Mathfield.config.createHTML" - // ] - // } - // } - // ], - "@typescript-eslint/triple-slash-reference": 0, - "no-restricted-globals": [ - "error", - "postMessage", - "blur", - "focus", - "close", - "frames", - "self", - "parent", - "opener", - "top", - "length", - "closed", - "location", - "origin", - "name", - "locationbar", - "menubar", - "personalbar", - "scrollbars", - "statusbar", - "toolbar", - "status", - "frameElement", - // "navigator", - "customElements", - "external", - "screen", - "innerWidth", - "innerHeight", - "scrollX", - "pageXOffset", - "scrollY", - "pageYOffset", - "screenX", - "screenY", - "outerWidth", - "outerHeight", - "devicePixelRatio", - "clientInformation", - "screenLeft", - "screenTop", - "defaultStatus", - "defaultstatus", - "styleMedia", - "onanimationend", - "onanimationiteration", - "onanimationstart", - "onsearch", - "ontransitionend", - "onwebkitanimationend", - "onwebkitanimationiteration", - "onwebkitanimationstart", - "onwebkittransitionend", - "isSecureContext", - "onabort", - "onblur", - "oncancel", - "oncanplay", - "oncanplaythrough", - "onchange", - "onclick", - "onclose", - "oncontextmenu", - "oncuechange", - "ondblclick", - "ondrag", - "ondragend", - "ondragenter", - "ondragleave", - "ondragover", - "ondragstart", - "ondrop", - "ondurationchange", - "onemptied", - "onended", - "onerror", - "onfocus", - "oninput", - "oninvalid", - "onkeydown", - "onkeypress", - "onkeyup", - "onload", - "onloadeddata", - "onloadedmetadata", - "onloadstart", - "onmousedown", - "onmouseenter", - "onmouseleave", - "onmousemove", - "onmouseout", - "onmouseover", - "onmouseup", - "onmousewheel", - "onpause", - "onplay", - "onplaying", - "onprogress", - "onratechange", - "onreset", - "onresize", - "onscroll", - "onseeked", - "onseeking", - "onselect", - "onstalled", - "onsubmit", - "onsuspend", - "ontimeupdate", - "ontoggle", - "onvolumechange", - "onwaiting", - "onwheel", - "onauxclick", - "ongotpointercapture", - "onlostpointercapture", - "onpointerdown", - "onpointermove", - "onpointerup", - "onpointercancel", - "onpointerover", - "onpointerout", - "onpointerenter", - "onpointerleave", - "onafterprint", - "onbeforeprint", - "onbeforeunload", - "onhashchange", - "onlanguagechange", - "onmessage", - "onmessageerror", - "onoffline", - "ononline", - "onpagehide", - "onpageshow", - "onpopstate", - "onrejectionhandled", - "onstorage", - "onunhandledrejection", - "onunload", - "performance", - "stop", - "open", - "print", - "captureEvents", - "releaseEvents", - // "getComputedStyle", - "matchMedia", - "moveTo", - "moveBy", - "resizeTo", - "resizeBy", - "getSelection", - "find", - "createImageBitmap", - "scroll", - "scrollTo", - "scrollBy", - "onappinstalled", - "onbeforeinstallprompt", - "crypto", - "ondevicemotion", - "ondeviceorientation", - "ondeviceorientationabsolute", - "indexedDB", - "webkitStorageInfo", - "chrome", - "visualViewport", - "speechSynthesis", - "webkitRequestFileSystem", - "webkitResolveLocalFileSystemURL", - "openDatabase" - ], - - // Turn off rules that are Typescript specific - // (they are turned on later in the "overrides" section) - "@typescript-eslint/explicit-module-boundary-types": "off", - - "no-unused-expressions": "off", - - "prettier/prettier": [ - "error", - { - "endOfLine": "auto" - } - ], - - // note you must disable the base rule as it can report incorrect errors - "indent": "off", - - "no-const-assign": "warn", - "no-this-before-super": "warn", - // "no-undef": "warn", - "no-unreachable": "warn", - "no-unused-vars": 0, // ["warn", { "argsIgnorePattern": "^_" }], - "constructor-super": "warn", - - "block-scoped-var": "error", - - "array-bracket-spacing": ["error", "never"], - "block-spacing": ["error", "always"], - "brace-style": 0, //["warn", "1tbs", { "allowSingleLine": true }], - "prefer-const": "warn", - - "valid-typeof": "error", - "consistent-return": "error", - "curly": ["warn", "multi-or-nest"], - "eqeqeq": ["error", "smart"], - "guard-for-in": "warn", - // "max-len": "warn", - "new-cap": "warn", - "no-bitwise": "off", - "no-console": "off", - "no-dupe-class-members": "off", // Support TypeScritp polymorphism - "no-else-return": "warn", - "no-eval": "error", - "no-fallthrough": "error", - "no-invalid-this": "warn", - "no-lone-blocks": "warn", - "no-return-assign": ["warn", "always"], - "no-redeclare": "off", - "no-self-compare": "error", - "no-sequences": "warn", - "no-unneeded-ternary": "warn", - "no-useless-call": "warn", - "no-var": "error", - "no-with": "error", - // "vars-on-top": "warn", - "no-delete-var": "warn", - "no-mixed-spaces-and-tabs": ["warn", "smart-tabs"], - "require-jsdoc": "off", - "space-infix-ops": ["error", { "int32Hint": false }], - - "no-trailing-spaces": [1, { "skipBlankLines": true }] - }, - "overrides": [ - { - // Typescript-specific rules - "files": ["*.ts"], - "rules": { - "@typescript-eslint/no-unused-vars": [ - "warn", - { - "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_", - // "vars": "all", - "args": "after-used", - "ignoreRestSiblings": false - } - ], - // We use @ts-ignore to generate the .d.ts files using tsc, even though - // they import .less file which tsc doesn't know to handle. - "@typescript-eslint/ban-ts-comment": "off", - - // When used with optional chaining, eslint can give an incorrect warning - "@typescript-eslint/no-unused-expressions": "warn", - "@typescript-eslint/prefer-optional-chain": "warn", - "@typescript-eslint/prefer-nullish-coalescing": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/naming-convention": [ - "warn", - { - "selector": "typeParameter", - "format": ["PascalCase"] - } - ], - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-use-before-define": [ - "error", - { - "functions": false, - "classes": false, - "variables": false, - "typedefs": false - } - ], - "@typescript-eslint/no-this-alias": [ - "error", - { - "allowedNames": ["self", "that"] // Allow `const self = this`; `[]` by default - } - ], - "@typescript-eslint/no-for-in-array": "error", - - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/explicit-module-boundary-types": "warn", - "@typescript-eslint/no-unnecessary-type-arguments": "warn", - // "@typescript-eslint/typedef": [ - // "warn", - // { - // "parameter": true, - // "arrowParameter": false, - // "variableDeclaration": false - // } - // ], - - "@typescript-eslint/array-type": ["warn", { "default": "array" }], - - "@typescript-eslint/no-explicit-any": "off" - } - } - ] -} diff --git a/.gitignore b/.gitignore index 8b27890b5..9c4140a2b 100755 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ Thumbs.db /test-results/ /playwright-report/ /playwright/.cache/ + +/.serena/ diff --git a/.vscode/settings.json b/.vscode/settings.json index beaf5e28c..1c4387257 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -50,5 +50,6 @@ "overrideConfigFile": ".eslintrc.json" }, "typescript.tsdk": "./node_modules/typescript/lib", - "prettier.configPath": "./node_modules/@cortex-js/prettier-config/index.json" + "prettier.configPath": "./node_modules/@cortex-js/prettier-config/index.json", + "liveServer.settings.port": 5502 } diff --git a/CHANGELOG.md b/CHANGELOG.md index c0fff7b10..c3db51173 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,530 @@ ## [Unreleased] +### Breaking Changes + +- The `@cortex-js/compute-engine` package is no longer a dependency of + MathLive. The Compute Engine was never bundled with MathLive; it was only + used for its TypeScript types. If your application loads the Compute Engine + (for example with `import "https://esm.run/@cortex-js/compute-engine"`), + MathLive continues to pick it up from the global scope and + `MathfieldElement.computeEngine` still works, but it is now typed as `any`. + Applications that need the types should add the package to their own + dependencies. +- The `mathfield.expression` getter and setter have been removed. Use + `MathfieldElement.computeEngine.parse(mf.getValue())` to obtain a boxed + expression, and `mf.setValue(expr.latex)` (or `mf.setValue(expr)`, which + still accepts a MathJSON object) to set one. +- The `"math-json"` output format has been removed from `getValue()`. Use + `MathfieldElement.computeEngine.parse(mf.getValue()).json` instead. +- The **Evaluate**, **Simplify** and **Solve** items have been removed from the + default context menu. + +## 0.110.0 _2026-06-08_ + +### Security Advisories + +- **#3028** Fixed a cross-site scripting (XSS) vulnerability in the rendering of + text-mode content. The body of `\text{}`, `\mbox{}` and similar commands + accepts arbitrary characters, which were reflected **unescaped** into both the + HTML markup (`convertLatexToMarkup()`, the editor, and the `` / + `` static elements) and the MathML output + (`convertLatexToMathMl()`). Input such as + `\text{}` could therefore execute arbitrary + JavaScript when the output was inserted into the DOM. Text content and + delimiters are now HTML-escaped in both output paths. Applications that render + untrusted LaTeX should upgrade. + +### Resolved Issues + +- **#2949** Fixed the layout of fill-in-the-blank prompts that contain content. + The prompt's outline box now correctly encloses and aligns with its content, + including tall content such as fractions, with improved vertical alignment. + This regression was introduced by the fix for **#2515**; note that **#2515** + (placeholders inside accent commands such as `\vec{}` cannot be filled) + remains unresolved and is tracked separately. (contributed by + @Scienthousiaste) + +- **#2963** Fixed font style (variant) changes being ignored for the first + character of an expression (or block). Applying a font style such as Roman + Upright to the first character now serializes correctly (e.g. `\mathrm{H}` + instead of a bare `H`), matching what is displayed in the mathfield. + (contributed by @psiservices-uwidmark) + +- **#2964** Fixed duplicate atoms accumulating when `setValue()` is called + repeatedly with incomplete LaTeX (such as `\sum_{`). Partially-formed atoms + that contain no content are now included when collecting atoms in a range, so + they are cleared on each sync instead of accumulating across update cycles. + (contributed by @Meinzzzz) + +- **#3030** Fixed TypeScript 6 compilation errors (TS2882) in the published type + declarations. The generated `.d.ts` files no longer contain bare side-effect + imports (such as `import '../core/modes'`) that referenced modules not + included in the package, which TypeScript 6 flags by default via + `noUncheckedSideEffectImports`. + +### Improvements + +- **#3032** `validateLatex()` now accepts an optional `options` argument with a + `macros` dictionary, so custom macros are recognized during validation instead + of being reported as unknown commands. (contributed by @Wigny) + +- **#3009** The types for static math elements are now exported from the package + entry point (`math-static-elements`), making them available to TypeScript + consumers. (contributed by @tsuji-riya) + +## 0.109.2 _2026-04-28_ + +### Resolved Issues + +- **#2931** Fixed virtual keyboard recursion in sandboxed iframe mathfields. The + message handling guard now checks the `source` window rather than only the + top-level window, preventing the local mathfield from re-dispatching its own + command messages and avoiding the resulting infinite loop. (contributed by + @konbu310) + +- Fixed duplicated safe area padding below the virtual keyboard. The + `--_keyboard-height` calculation no longer adds `env(safe-area-inset-bottom)` + on top of `--_padding-bottom`, which already includes it; this removes the + doubled empty space that appeared below the keyboard on devices with bottom + safe areas. (contributed by @Blibbo) + +- Fixed a stacking issue in the color picker where the background of neighboring + color swatches could paint over the border of the active swatch. The focused + color now has an explicit `z-index` to keep it above its neighbors. + (contributed by @Blibbo) + +### Improvements + +- Added Catalan (`ca`) localization. (contributed by @aaronFortuno) + +## 0.109.1 _2026-04-01_ + +### Resolved Issues + +- **#2974** Fixed empty/unresponsive mathfield after inserting a fraction + following a parenthesized expression built via keyboard (e.g. + `5 ) Home ( End /`). Also fixed a phantom duplicate caret that appeared after + the same input sequence. (contributed by @zojize) + +## 0.109.0 _2026-03-11_ + +### Resolved Issues + +- **#2946** Fixed virtual keyboard toggle focus issues. Clicking the + `virtual-keyboard-toggle` button now correctly focuses the mathfield and + accepts virtual keyboard input. (contributed by @mgreminger) + +- **#2956** Fixed incorrect inter-atom spacing before relation operators (e.g. + `=`) when using accent commands like `\hat{x}`. (contributed by @repst) + +- **#2957** Fixed a caret trap in LaTeX command mode after deleting a lone + backslash. Empty `latexgroup` containers are now removed when delete + operations remove the last LaTeX atom. (contributed by @repst) + +- **#2958** Fixed smart-fence closing delimiter remaining in ghost styling after + pressing Space. The closing delimiter is now properly committed and rendered + normally. (contributed by @repst) + +- Fixed the caret getting trapped inside a LaTeX command group when navigating + with arrow keys. Pressing left or right arrow to move past the boundary of a + `\command` now correctly accepts the command and exits LaTeX mode. + +### Improvements + +- **#2935** Fixed Korean localization translation errors. (contributed by + @onu032001) + +- **#2938** Removed debug `console.log` from `PromptAtom`. (contributed by + @remiangot) + +- **#2726** Updated Italian localization with improved translations and + capitalization normalization. (contributed by @GGilli) + +## 0.108.3 _2026-02-11_ + +### Resolved Issues + +- **#2906** Fixed a runtime error + (`undefined is not an object (evaluating 'this.ariaLiveText.textContent=""')`) + that occurred when the mathfield element could not be fully initialized (e.g. + in Vue runtime-only builds) or after the mathfield was disposed. + +## 0.108.2 _2025-11-13_ + +### Resolved Issues + +- **#2899** Typing an inline shortcut immediately after inserting a structure + (e.g. `sin/cos`) now expands the shortcut correctly and keeps the caret in the + numerator placeholder. The inline shortcut buffer is flushed in place, so the + first character typed in a fresh placeholder is tracked again, and genfrac + insertion prioritizes the numerator placeholder before falling back to the + denominator. This fixes both the missing `\cos` expansion and the regression + where the denominator was erroneously selected after `/`. + +## 0.108.1 _2025-11-11_ + +### New Features + +- **Responsive toggle button layout**: The virtual keyboard toggle and menu + button now automatically switch from horizontal (side-by-side) to vertical + (stacked) layout when the mathfield height is 100px or greater. This happens + dynamically as you add content to the mathfield. + +### Resolved Issues + +- Fixed virtual keyboard toggle requiring triple-click to activate. +- **#2892** Fixed vertical alignment issues when using `\colorbox` with + expressions that have both subscripts and superscripts. Previously, applying a + background color to an expression like `N_{k}^{2019}` would cause the + subscripts and superscripts to be misaligned or shift upward. Colorboxes now + render correctly with proper baseline alignment regardless of whether the + content has only a subscript, only a superscript, or both. + +## 0.108.0 _2025-11-09_ + +### New Features + +- Virtual keyboard keycaps now expose stable automation hooks. Each keycap + includes an `aria-label` and `data-keycap-value` alongside its serialized + command, allowing assistive tech and automated tests to target keys by their + semantic value instead of brittle glyph text. Playwright e2e tests were + updated to drive the keyboard using these attributes. +- Added `` and `` web components for lightweight static + rendering of mathematical expressions. These components provide an alternative + to `renderMathInDocument()` with a simpler API and support for inline + (``) and block-level (``) formulas. They support LaTeX, + AsciiMath, and MathJSON input formats, and offer a `render()` method for + manual re-rendering. + - **Performance optimizations**: Lazy font loading (fonts load once globally), + Intersection Observer for deferred rendering (formulas render only when + visible) + - **Accessibility enhancements**: Auto-generated ARIA labels with speakable + text, MathML fallback for screen readers, keyboard navigation support when + focusable (Space/Enter to speak formula), automatic `role="img"` attribute +- **#2732** Pressing the Space key in LaTeX mode now completes the LaTeX command + and exits LaTeX mode, similar to pressing Enter. Previously, users typing + LaTeX commands like `\alpha` would remain in LaTeX mode until pressing Enter, + and incomplete commands would be lost when dismissing dialogs. Now pressing + Space after a valid LaTeX command completes it and returns to math mode, + making LaTeX input more intuitive and preventing data loss. Additionally, when + typing LaTeX commands with mandatory arguments (like `\frac{1}{2}`), the + command now auto-completes and exits LaTeX mode when the closing brace of the + last mandatory argument is typed. The implementation uses the command registry + to determine the exact number of mandatory arguments, ensuring correct + behavior for all commands including those with nested arguments. +- **#2786** Fixed scientific notation handling when creating fractions. When + typing scientific notation like `5e-2` or `3.14×10^{-2}` and then pressing `/` + to create a fraction, the entire expression now stays together in the + numerator instead of being split apart. Previously, only part of the number + would be included (e.g., just `2` from `5e-2`), breaking the scientific + notation. The fix recognizes both e-notation (`5e-2`, `3.14e+10`) and ×10^ + notation (`3.14×10^{-2}`, `5×10^3`) as atomic units. +- **Scientific notation template auto-formatting**. When typing scientific + notation in e-notation format (e.g., `3.14e2`, `5E-3`, `1.23e+10`), MathLive + now automatically formats it using a customizable template. The formatting is + triggered either when typing a non-digit character after the notation or after + a brief pause in typing (controlled by `inlineShortcutTimeout`). The template + is configured via the `scientificNotationTemplate` property (default: + `#1\times10^{#2}`) where `#1` is replaced with the significand and `#2` with + the exponent. For example, typing `3.14e2` followed by a space will + automatically format to `3.14×10²`. The feature respects the + `decimalSeparator` setting for internationalization. + +### Resolved Issues + +- Fixed click positioning in multiline environments. When clicking in a + multiline environment (such as `\displaylines`, `gather`, `align`, etc.), the + caret is now correctly positioned on the line that is closest to the click + point vertically, rather than selecting atoms from other lines based on + overall Euclidean distance. For example, when clicking horizontally below an + atom on line 1 but vertically at the level of line 2, the caret will now be + positioned on line 2 instead of incorrectly jumping to line 1. This fix + ensures that clicking in multiline content respects the visual line structure, + making editing more intuitive and predictable. +- Fixed MathML serialization for multiline mathfields. Previously, serializing + multiline mathfields (such as `\displaylines{\\ }`) to MathML would result in + invalid MathML with syntax errors and console warnings. The fix corrects a + syntax error in array delimiter serialization and properly handles line break + (`\\`) atoms in multiline environments by skipping them (since line breaks are + represented by the `` table structure in MathML, not by explicit line + break atoms). +- **#2146** Fixed nested subscript and superscript creation when using physical + keyboard shortcuts (`Shift+-`, `Ctrl+-`). Previously, typing `a_b_` would + highlight `b` and reuse the outer subscript instead of creating a subscript of + `b`, and typing `a^b_` inside the exponent would unexpectedly attach the new + subscript to `a`. Script commands now attach to the atom that currently has + focus, so nested scripts work regardless of whether they are triggered by + synthetic text insertion or by real key combos. +- **#2343** Fixed clipboard API errors when MathLive is used in sandboxed + iframes. Previously, when pasting content in a mathfield embedded in a + sandboxed iframe without `allow="clipboard-read; clipboard-write"` + permissions, unhandled promise rejections would appear in the console + ("Permissions policy violation: The Clipboard API has been blocked"). The + paste operation now gracefully handles clipboard API errors with appropriate + user feedback. +- **#2364** Fixed `--keycap-height` CSS custom property not working in sandboxed + iframes. Previously, setting `--keycap-height` to custom values (e.g., 15px) + had no effect because responsive breakpoint queries were using `max()` + functions that enforced minimum height values. The custom property now + properly accepts any user-defined value while maintaining sensible defaults + (60px, 52px, 42px) when no custom value is provided. +- **#2391** Fixed spacing for `\class`, `\cssId`, `\htmlId`, `\htmlData`, + `\htmlStyle`, and `\href` commands. These commands now properly respect + mathematical spacing rules, matching the behavior of similar commands like + `\textcolor`. Previously, expressions like `1=\class{test}{1}` would not have + proper spacing before the wrapped element, while `1=\textcolor{red}{1}` would. + The commands now create atoms with the correct type (`mord`) and use deferred + argument parsing to ensure proper inter-atom spacing. +- **#2419** Fixed parsing of dollar-delimited math expressions when using + `insert()` in text mode. Previously, when inserting strings like + `"La fonction $f$ est croissante"` into a mathfield with `defaultMode="text"`, + the dollar signs and their content were being escaped, preventing proper math + mode switching. The text mode editor now correctly preserves math regions + delimited by `$...$` or `$$...$$` while only escaping special characters in + text regions, allowing mixed text and inline math expressions to be inserted + correctly. +- **#2444** Font style menu items (roman, italic) are now always visible and + properly toggleable. Previously, these items only appeared when text was + selected, and toggling them when positioned after styled text would not work + on the first click. The toggle logic now correctly checks against the computed + insert style (including inherited styles from adjacent atoms) rather than just + the explicit default style, allowing users to easily toggle off font styles + like `\mathrm{}` (roman/upright). +- **#2447** When deleting content in a subscript or superscript of operators + like `\lim`, the cursor would become trapped in the empty branch. Now, when + all content in a subscript/superscript is deleted and the delete key is + pressed again, the empty branch is removed and the cursor properly navigates + out, while keeping the operator itself intact. +- **#2476** Readonly expressions can no longer be modified using the virtual + keyboard. Previously, when a mathfield was readonly or when the selection was + within a readonly portion (such as outside a prompt/placeholder in a + fill-in-the-blank scenario), typing on the virtual keyboard would incorrectly + replace or modify the readonly content. The virtual keyboard now respects + readonly protection, matching the behavior of the physical keyboard by playing + a "plonk" sound and rejecting the input. +- **#2480** Fixed selection behavior within `\mathtip` and `\texttip` commands. + Previously, it was only possible to select the entire content of a mathtip at + once (e.g., all of `x+1` in `\mathtip{x+1}{test}`), even though navigation + with arrow keys worked correctly. Now partial selection within tooltip content + is properly supported, allowing users to select individual characters or + subexpressions. Additionally, the tooltip styling has been corrected to + properly display the background color, border, and shadow on hover. +- **#2512** Fixed excessive blank space appearing above inline `array` + environments. The vertical alignment of arrays has been adjusted to align the + first row's baseline with surrounding text rather than centering the entire + array around the math axis, which was causing unwanted spacing above inline + arrays with column separators like + `\begin{array}{l|l} a & b\\ c & d\end{array}`. +- **#2515** Fixed placeholders inside accent commands (`\vec{}`, `\bar{}`, + `\hat{}`, etc.) not being clickable. When clicking on a placeholder wrapped in + an accent command, the cursor now correctly positions inside the placeholder + instead of selecting the entire accent atom. +- **#2521** Fixed the `/` shortcut misplacing subscripts when converting a + selection into a fraction. Previously, selecting a symbol with an attached + subscript, e.g. `d_0`, and pressing `/` would move only the base `d` into the + numerator and leave the orphaned `_0` attached to the denominator placeholder. + Selection normalization now automatically expands to include the associated + `subsup` atom so the scripted symbol stays intact inside the resulting + fraction. +- **#2558** Fixed cursor jumping to the beginning when entering a left + parenthesis before a previously entered right parenthesis. When typing a + closing bracket first (e.g., `1+2+3)`), then moving the cursor back and typing + an opening bracket `(`, the smart fence feature would correctly create + `\left(2+3\right)` but would incorrectly position the cursor at the start of + the expression instead of after the opening parenthesis. The cursor now + properly stays positioned inside the newly created fence, right after the left + delimiter. +- **#2547** Fixed rendering of `\colorbox` in fractions where the colored + background would obscure the fraction bar. The background is now rendered + behind the content using a CSS pseudo-element with `z-index: -1`, preventing + it from painting over adjacent elements like fraction bars. This issue + occurred specifically with double-braced content like + `\frac{{\colorbox{red}{344}}}{3}`. +- **#2570** Fixed `\bf`, `\it`, and related text formatting commands not + accepting braced arguments. Previously, commands like `\bf{425}` would fail to + parse because the parser didn't handle braced arguments for `{:rest*}` + deferred parameters. Additionally, the rendering system wasn't properly + combining `fontFamily` with `fontSeries`/`fontShape` properties, causing + `\bf{425}` to render in regular weight instead of bold. The parser now + correctly handles braced arguments, and the `getFont()` method builds compound + variant keys (e.g., 'roman-bold') by combining these style properties. +- **#2572** Fixed typing special characters in placeholders. When typing + characters with keybindings (like backslash, parentheses, brackets) in a + selected placeholder, the characters would disappear or trigger unwanted + behaviors. The placeholder is now deleted before keystroke processing, + allowing all keybindings and character handling to work correctly. +- **#2579** Fixed multiple mathfields showing blinking cursors simultaneously + when `focus()` is called rapidly on multiple mathfields. This occurred because + Chromium browsers don't fire blur events reliably during rapid focus changes, + causing multiple mathfields to remain in a focused state. The fix implements + global focus tracking to ensure only one mathfield can be focused at a time, + explicitly blurring any previously focused mathfield when a new one gains + focus. This handles browser blur event quirks while maintaining backward + compatibility with all existing focus/blur behavior. +- **#2619** Fixed placeholders in multi-row array environments (like + `\displaylines`) not being focusable with pointer clicks. The hit-testing + logic now properly determines which row was clicked before searching for + atoms, ensuring placeholders in different rows can be correctly selected. + Additionally, fixed prompt hit testing to include the full visual area + (including padding) and corrected cursor positioning to place the cursor + inside the prompt body rather than selecting around it. +- **#2665** Clicking on a mathfield now only fires focus events once, instead of + generating duplicate focus/blur/focusin/focusout events. +- **#2668** Fixed right-click context menu causing MathField to lose focus when + clicking on formatted text. Previously, right-clicking on equation text would + blur the field, potentially causing incorrect rendering when selecting menu + options like color changes. The context menu event is now prevented before + showing the menu, ensuring focus is maintained regardless of where the + right-click occurs. +- **#2685** Fixed physical keyboard not working after programmatic focus with + manual virtual keyboard policy. When calling `mf.focus()` programmatically at + page load with `mathVirtualKeyboardPolicy: "manual"`, the physical keyboard + would stop working. This was caused by a double `onFocus()` invocation that + set up event capture listeners at the wrong time. The fix prevents the second + `onFocus()` call when focus is initiated programmatically, ensuring keyboard + events are properly routed. +- **#2686** When deleting a range that includes special mathematical atoms like + `\sqrt`, `\frac`, or `\enclose`, the special atom structure is now properly + removed or cleaned up. Previously, deleting content would leave behind empty + special atom structures. Now, if a special atom becomes empty after deletion, + it is removed entirely. Additionally, when a deletion crosses the boundary of + a special atom (e.g., selecting content both outside and inside a square + root), any remaining content inside the special atom is hoisted out, and the + special atom structure is removed. +- **#2698** Dispatched virtual keyboard events now include the standard `origin` + property when `mathVirtualKeyboardPolicy = "sandboxed"`. Previously, when + MathLive operated within an iframe in sandboxed mode, the MessageEvent objects + dispatched via `window.dispatchEvent()` were missing this property, causing + validation errors in environments that check for the origin domain. +- **#2714** Disabled mathfields no longer receive focus or accept keyboard + input. Previously, clicking on or calling `.focus()` on a disabled mathfield + would trigger focus events and allow keyboard input like `{` and `/` to modify + the content, despite the documented behavior that disabled mathfields should + not be focusable. Disabled mathfields are now properly non-focusable, + preventing all focus events and keyboard input. +- **#2728** Fixed missing spacer elements when using the `\class` command. + Previously, expressions like `x+\class{highlight}{y}+1` would not have proper + spacing before the styled content, causing the spacer element between the `+` + operator and the wrapped content to be missing. The `\class` command now uses + `boxType: 'lift'` to properly expose its contents to the inter-box spacing + algorithm, matching the behavior of similar styling commands like `\emph`. +- **#2733** Fixed inline shortcut buffer persistence after deleting all content. + When typing characters that triggered an inline shortcut (e.g., "xx" → + "\times"), then deleting all content with Backspace, and typing again, the + stale shortcut buffer would incorrectly trigger shortcuts on new input. The + inline shortcut buffer is now properly flushed when the mathfield becomes + empty after a deletion operation. +- **#2739** Fixed a crash when pressing Backspace on an empty `\displaylines` + environment. The error "Cannot read properties of undefined (reading + 'length')" would occur when creating a multiline display (Cmd+Enter), deleting + all content, and pressing Backspace again. The row deletion logic now properly + handles empty cells and missing rows. +- **#2754** Setting the `placeholder` property programmatically now works + correctly. Previously, `mathfield.placeholder = "text"` would not update the + placeholder. The property now follows the same pattern as all other properties + for consistent behavior. +- **#2767** When calling `getValue()` on a mathfield in multiline mode with a + range that is only within a single line, the returned value is no longer + unnecessarily wrapped in a `\displaylines{}` command. The wrapper is now only + added when the content actually spans multiple lines. +- **#2770** Fixed cursor positioning on first click in complex formulas. + Previously, clicking to position the cursor in formulas with many nested + elements (fractions, subscripts, superscripts) would fail until keyboard + navigation was used first. The atom bounds cache is now properly maintained + across interactions, ensuring accurate cursor positioning from the first + click. +- **#2771** The `` element now properly shrinks back to its original + size after deleting large multi-line formulas (e.g., `\begin{align}` + environments). Previously, when clearing content from a multi-row structure, + the empty rows would be preserved, causing the field to remain at the expanded + height. Now, when all content is deleted, the structure is reset to a single + empty row while preserving the environment type. +- **#2784** The `\hat{}` accent is now properly centered over uppercase letters. + Previously, the accent would appear shifted rather than centered when applied + to uppercase characters, while it worked correctly with lowercase letters. The + centering algorithm now accounts for the width difference between the base + character and the accent to ensure proper positioning for all characters. +- **#2799** When selecting content inside mathematical structures (square roots, + brackets, etc.) using the mouse and then inserting a fraction, the selected + content no longer disappears. The selection is now properly used as the + numerator of the fraction. +- **#2816** Programmatic calls to `.focus()` now correctly trigger `focusin` + events. +- **#2818** The `\mathrm` command and other upright variants (`\mathsf`, + `\mathtt`) are now correctly preserved when exporting to the `latex-expanded` + format. Previously, these commands were being stripped from the output, + causing `\mathrm{d}` to be serialized as just `d`. +- **#2832**, **#2727** Fixed rendering of `\cancel`, `\bcancel`, `\xcancel`, and + other SVG-based enclosure notations (diagonal strikes, horizontal/vertical + strikes, etc.). In v0.106.0+, these notations became completely invisible due + to SVG elements having 0 width in their viewBox. Additionally, when visible, + the positioning of strike lines was incorrect, particularly for complex + expressions and fractions. The fix restores proper width calculations and + implements symmetric padding for notation boxes, using em-based coordinates + with small margins for accurate line positioning. Strike lines now properly + cover content from corner to corner without extending too far or appearing + offset. +- **#2847** Fixed scroll behavior in fixed-height mathfield containers. When + navigating with arrow keys or Tab/Shift+Tab to move between placeholders, the + container now properly scrolls to keep the cursor or selection visible. + Previously, selecting placeholders would cause the scroll to jump to the top, + and navigating upward wouldn't scroll when the cursor was on the first line. + The fix also adds padding (20px) so scrolling happens before the cursor + reaches the very edge of the viewport, and ensures full selections are visible + rather than just showing 1 pixel. +- **#2849** For compatibility with KaTeX, do not wrap the argument of delimiter + commands. +- **#2851** Fixed multiline row merging when deleting across multiple lines. + Previously, when a selection spanned multiple rows in a multiline environment + (like `\displaylines`) and was deleted, the remaining content would stay on + separate lines instead of merging. Now, when deleting across rows, the + remaining fragments are properly merged into a single line. +- **#2859** When using `mathVirtualKeyboardPolicy="manual"`, controlling the + virtual keyboard via `focusin`/`focusout` event listeners now works correctly. + The keyboard no longer closes immediately after opening. +- **#2867** In some cases, applying a superscript or subscript to a command with + an argument (for example `\mathbb{R}`) would incorrectly include the argument + in the superscript or subscript. +- **#2884** The command to add/remove columns and rows when in a `cases` + environment are now enabled. +- **#2889** Custom attributes would not be propagated to the generated markup. +- Fixed double-click selection of styled text. Previously, double-clicking on + styled content like `\mathrm{xyz}` would incorrectly select adjacent unstyled + text instead of the styled content itself. The selection boundary logic now + properly checks the next atom's style before advancing, preventing off-by-one + errors when determining style boundaries. +- **#2892** Fixed superscripts and subscripts shifting upward when inside a + `\colorbox` command. Elements with background colors now receive proper + vertical alignment (`vertical-align: -depth`) to prevent positioning issues + with content of different heights. The fix also ensures background colors use + the correct color palette (background colors instead of foreground colors) and + maintains editability via the background color menu. Selection highlighting no + longer causes subscripts to shift, as vertical alignment is only applied to + intentional background colors, not selection highlights. +- Fix empty-line selection highlighting in multiline mathfields + +## 0.107.1 _2025-09-30_ + +### Resolved Issues + +- **#2817** Calling `setPromptValue()` no longer changes the focus to the + mathfield. +- **#2805** On mobile, show the math virtual keyboard when using prompts +- Custom macros are now read when using speakable text to speech. +- **#2824** Using TypeScript would trigger a compile error about + `Cannot find module '../editor/keyboard-layout'` + +## 0.107.0 _2025-08-17_ + +### New Features + +- Added `mountMathVirtualKeyboard()` to control in which browsing context the + math virtual keyboard is mounted. + +### Resolved Issues + +- **#2800**: New line on matrix causes duplicated input + ## 0.106.0 _2025-07-11_ ### New Features @@ -158,7 +683,6 @@ MathfieldElement.createHTML = (html) => DOMPurify.sanitize(html); - Generate only standard trigonometric functions, i.e. those available in the `amsmath` package. Use `\operatorname{}` for the others. The standard commands are: - - `\arccos` - `\arcsin` - `\arctan` @@ -313,7 +837,6 @@ For example: definition. The keycaps are one of these special shortcuts: - - `[left]`, `[right]`, `[up]`, `[down]`, `[return]`, `[action]`, - `[space]`, `[tab]`, `[backspace]`, `[shift]`, - `[undo]`, `[redo]`, `[foreground-color]`, `[background-color]`, @@ -528,7 +1051,6 @@ numbers. Added CSS variables to control the appearance of the toolip displayed with `\mathtip` and `\texttip`: - - `--tooltip-border` - `--tooltip-color` - `--tooltip-background-color` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index de9f1a5b4..ee50853f8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,8 +31,8 @@ test case that demonstrates the issue, if applicable. Sure! Have a look at the issue report, and make sure no one is already working on it. If the issue is assigned to someone, they're on it! Otherwise, add a comment in the issue indicating you'd like to work on resolving the issue and go -for it! See the [Contributor Guide](documentation/CONTRIBUTOR_GUIDE.md) for -coding guidelines. +for it! See the [Language and Coding Style](#language-and-coding-style) section +for coding guidelines. ## Contributing Test Cases @@ -41,8 +41,8 @@ not introduced as new features are added (regression). Adding or updating test cases can be very helpful to improve MathLive's quality. Submit an issue indicating what you'd like to work on, and a pull request when -you have it ready. Test cases should follow the TAP (Test Anything Protocol) -format. +you have it ready. The test suite uses [Playwright](https://playwright.dev/) for +browser tests and standard `.test.ts` files for unit tests. ## Contributing Ideas and Feature Requests @@ -68,57 +68,390 @@ branch. Congratulations, you've become a MathLive contributor! Thanks for your help! -## Build Instructions +## Setting up Your Development Environment -Before you can do a local build of MathLive, you need to have the following -dependencies installed: +The project uses [NPM scripts](https://docs.npmjs.com/misc/scripts) for its +build system. The `package.json` file and the `scripts/` directory contain the +definitions of the build scripts. -- [npm](https://www.npmjs.com/), which will also install - [Node.js](https://nodejs.org) -- [Playwright](https://playwright.dev/), to run the test suite -- If you're using Windows, you will need to install the `bash` shell. The `bash` - shell is required and pre-installed on macOS and Linux. For instructions on - how to install `bash` on Windows, see the - [this article](https://www.howtogeek.com/249966/how-to-install-and-use-the-linux-bash-shell-on-windows-10/) +To get started developing: -Now that you have the dependencies installed, you can -[fork and clone](https://docs.github.com/en/get-started/quickstart/fork-a-repo) -this repository. - -Then, in the cloned project folder, use the following commands to start a local -dev server: +1. Install [Node.js](http://nodejs.org) on your dev machine (this will also + install `npm`). The LTS version is recommended. +2. If you're using Windows, you will need to install the `bash` shell. The + `bash` shell is required and pre-installed on macOS and Linux. For + instructions on how to install `bash` on Windows, see + [this article](https://www.howtogeek.com/249966/how-to-install-and-use-the-linux-bash-shell-on-windows-10/). +3. [Fork and clone](https://docs.github.com/en/get-started/quickstart/fork-a-repo) + this repository, then: ```bash -# Install dependencies -npm install +$ cd mathlive +$ npm ci +``` + +Depending on your system setup, you may need to run as admin, in which case use +`sudo npm ci` or equivalent. + +The `npm ci` command installs in the `mathlive/node_modules` directory all the +Node modules necessary to build and test the MathLive SDK. -# Run local dev server with live reload +Once the installation is successful, you can use the following commands: + +```bash +# Start a local dev server with live reload # After running this command, point your browser to http://127.0.0.1:9029/dist/smoke/ -npm run start +$ npm start + +# Make a local development build in the `dist/` directory +$ npm run build + +# Run test suite +$ npm test + +# Run the linter +$ npm run lint + +# Create a production build to `dist/` +$ npm run build production + +# Calculate the code coverage and output to `coverage/` +$ npm test coverage ``` -To run the test suite locally, run the following commands (if the dev server is -running, close it using Ctrl-C before running these commands): +During development, keep `npm start` running. A build will be triggered when a +source file is updated. Note however that changes to `.less` files do not +trigger a rebuild. You'll need to stop and restart `npm start`. + +### Running tests + +To run the test suite locally: ```bash -# Install playwright browsers -# This only needs to be done once for each version of playwright -# Additional installation of browser dependencies may be required, follow instructions +# Install playwright browsers (once per playwright version) npx playwright install -# Build the production version of MathLive +# Build, then run the full test suite npm run build - -# Run test suite npm test ``` -Note that, because of how the dev server manages files, `npm run build` needs to -be run before each `npm test` run. When debugging the Playwright browser tests, -the `npx playwright test` command can be used to run only the Playwright tests. -When running the Playwright tests directly, `npm run build` is not required. -Also, the Playwright tests, when run with `npx playwright test`, can be run -while the dev server is running. +Note that `npm run build` needs to be run before each `npm test` run. When +debugging the Playwright browser tests, `npx playwright test` can be used to +run only the Playwright tests — in that case `npm run build` is not required, +and the tests can run while the dev server is running. + +### Submitting changes + +Run the test suite with `npm test` and linter with `npm run lint` to make sure +your changes are ready to submit, then push a PR to the main branch. + +### Troubleshooting + +If you are getting build errors after updating your repo, your `node_modules/` +directory may need to be updated. Run: + +```bash +$ npm ci +``` + +## Code Structure + +The MathLive SDK consists of the following key directories: + +- `css/` the stylesheets and fonts +- `sounds/` the default sound files +- `src/core` the core layout engine: parsing LaTeX and rendering it +- `src/atoms` the specific "kinds" of atoms that can be displayed (fractions, + operators, groups, accents, delimiters, etc.) +- `src/latex-commands` the definitions for the specific LaTeX commands known + by MathLive +- `src/formats` conversion to other formats (MathML, ASCIIMath, spoken text, + MathJSON, Typst) +- `src/editor-model` the "state" of the mathfield (its model), including the + content and selection, and the code to modify it (insert, remove, etc.) +- `src/editor` utilities to handle user interaction, including keybindings, + shortcuts, localization, text-to-speech, and commands +- `src/editor-mathfield` the outer layer that handles user input and + interaction with the DOM +- `src/virtual-keyboard` the virtual keyboard implementation +- `src/ui` shared UI components (menus, icons, colors, geometry, i18n) +- `src/common` shared utilities and types +- `src/addons` optional add-ons (static rendering, definitions metadata) +- `src/public` the exposed public API of MathLive, including the + `MathfieldElement` wrapper class +- `dist/` contains executable build artifacts. If a file named + `DEVELOPMENT_BUILD` is present in the directory, the content is suitable only + for development purposes (not minified, includes `.map` files). + +The content of the `dist/` directory is entirely generated as part of the build +process. No other directory should contain intermediate files generated as part +of the build process. + +## Language and Coding Style + +MathLive is written in TypeScript. + +The project uses `prettier` and `eslint` to enforce consistent formatting and +coding style. They will be run automatically before commits. You can also run +them manually using `npm run lint`. + +The code base attempts to follow these general guidelines: + +- **Consistency** All code in the codebase should look as if it had been written + by a single person. Don't write code for yourself, but for the many people who + will read it later. +- **Clarity before performance** Write code that is easy to read, and avoid + obscure constructs that may obfuscate the code to improve performance. For + example, RegEx are crazy fast in all modern browsers, and trying to roll out + your own pattern matching will result in more code and less performance. If + you think something could be made faster, use + [http://jsben.ch/](http://jsben.ch/) to try out options in various browsers + and compare the results. You might be surprised. +- **Follow Postel's Law, the Robustness Principle** "Be conservative in what you + do, be liberal in what you accept from others". For example, functions that + are invoked internally do not need to check that the input parameters are + valid. However, public APIs should check the validity of parameters, and + behave reasonably when they aren't. + +## Links in Doc Comments + +Doc comments on exported declarations are published as `src/api.md` by +`npm run doc`, and mathlive.io renders that file at `/mathfield/api/`. Typedoc +rewrites links on the way out, and four rewrites turn a reasonable-looking +comment into a dead or silently wrong link. Docusaurus reports the dead ones; +nothing reports the wrong ones. + +- **Use `{@link}` for symbols, markdown for URLs.** `{@link}` resolves a symbol + name and nothing else. Given a path it fails quietly in the output: + `{@link mathfield/guides/speech/ | Guide: Speech}` is published as the literal + text `mathfield/guides/speech/ \| Guide: Speech`, not a link. Typedoc does + warn (`Failed to resolve link to ...`), so read the warnings from `npm run + doc`. +- **Never put a `#fragment` on a site-absolute link.** Typedoc strips the path + and keeps the fragment, so + `[CSS variables](/mathfield/guides/customizing/#css-variables)` is published + as `[CSS variables](#css-variables)` — a same-page link to a heading that does + not exist. A path with no fragment survives untouched, and so does a full URL. + Write `[the customizing guide](/mathfield/guides/customizing/)` or + `[CSS variables](https://mathlive.io/mathfield/guides/customizing/#css-variables)`. +- **Link to a type, not to a property of one.** A type alias is rendered as a + single code block, so its properties get no heading of their own and there is + nothing for a link to land on. Link to the type and name the property in the + prose. +- **Avoid reusing a name across exported symbols.** Anchors are the lowercased + member name, disambiguated by document order: a second `Style` becomes + `#style-1`. Links inside `api.md` are generated in the same pass and stay + consistent, but the numbering shifts whenever a same-named export is added, + removed or renamed — so anything outside the file that points at a numbered + anchor breaks silently. Distinct names give stable, readable anchors. + +`docs/mathfield/api.md` in the cortexjs.io repo is generated from this file and +is overwritten by every build. Fix a bad link here, not there. + +## Bundling + +The TypeScript code is compiled to JavaScript by the `tsc` compiler. When doing +a production build, the JavaScript is bundled and minimized with `esbuild`. The +CSS files are minimized with `postcss`. + +## Browser Support + +MathLive is designed for the modern web. Supporting older browsers complicates +the effort involved in building new features, but it is also an insecure +practice that should not be encouraged. + +In this context, _modern_ means the latest two releases of Chrome, Edge, Safari +and Firefox. Both desktop and mobile are supported. + +Note that the HTML quirks mode is not supported. This means that the host page +should use the strict mode, indicated by a `` directive at the +top of the page. + +Note that the HTML page should use the UTF-8 encoding. Use a server header or a +`` tag in the page if necessary. + +## Accessibility - A11Y + +### Rendering + +MathLive renders math using HTML and CSS. Digits, letters and math symbols are +displayed in `` tags with the necessary CSS styling to display them in the +right place. In addition, rules (lines) such as the fraction line, are rendered +using CSS borders. In a few rare cases, SVG is used to render some decorations, +such as the annotations of the `\enclose` command. + +The rendered math is not purely graphical, and as such can be accessed by screen +readers. + +### Alternate renditions + +In addition to the "visual" HTML+CSS representation that MathLive outputs, it +can also generate alternate renditions, including: + +- **LaTeX**: a string of LaTeX code equivalent to the formula. +- **Spoken Text**: a text representation of the formula as someone would speak + it, for example: `f(x) = x^2` → "f of x equals x squared" +- **Annotated Spoken Text**: as above, but in addition prosody hints are + inserted for a more natural rendition by text to speech systems (breathing + pauses, variation in pitch, etc...). + +Those alternate renditions are rendered as an ARIA-label, or as an element that +is not visually rendered, but visible to screen readers. + +### Speech + +Although MathLive works with screen readers, since math is its own language +MathLive has its own built-in text to speech renderer. With the speech interface +it is possible to: + +- read the current group (numerator or subscript, for example) + - Mac: `Ctrl + Command + Down` + - Windows/Linux/ChromeOS: `Ctrl + Alt + Down` +- read what's before or after the selection + - Mac: `Ctrl + Command + Left/Right` + - Windows/Linux/ChromeOS: `Ctrl + Alt + Left/Right` +- read the parent of the current group + - Mac: `Ctrl + Command + Up` + - Windows/Linux/ChromeOS: `Ctrl + Alt + Up` +- read the current selection + - Mac: `Ctrl + Command + Shift + Down` + - Windows/Linux/ChromeOS: `Ctrl + Alt + Shift + Down` + +With these convenient keyboard shortcuts, it is possible to aurally navigate and +understand even complex formulas. + +### Input and navigation + +MathLive supports multiple modalities for input: in addition to pointer devices +(mouse, trackpad, touch screen), MathLive has an extensive set of keyboard +shortcuts that allow navigation and editing of the most complex formulas. Every +operation is possible without the use of a pointing device. + +Conversely, it is possible to enter commands and complex mathematical symbols +using only a pointing device: the command bar can be invoked by tapping a round +toggle button displayed to the right of the formula. The command bar offers +large buttons that act as a virtual keyboard, but offer contextual operations +depending on the current selection, and the content around it. Those buttons are +easy to use on touch screens and for users of alternative pointing devices. + +## Architecture + +The core of MathLive is a rendering engine that generates HTML (and SVG) markup. +This engine uses the TeX layout algorithms because of their quality. Given the +same input, MathLive will render pixel for pixel (or very close to it) what TeX +would have rendered. + +To do so, it makes use of a web version of the fonts used by TeX and which are +included in the `dist/fonts/` directory. + +Although the rendering engine follows the TeX algorithms, MathLive also has an +in-memory data structure to represent a math expression while it is being edited +(a tree of `Atom`s). + +MathLive is divided into two main components: + +- Core: handles rendering of LaTeX to HTML markup +- Editor: handles the user interaction with the formula, using Core for the + rendering. + +### Core + +Core takes a LaTeX string as input. A `lexer` converts the string into `Token[]` +which are then passed on to a `parser`. The `parser` uses the information from +`modes-*` to parse the tokens depending on the current mode (text, math, +etc...). The LaTeX commands are defined in `latex-commands/`, and used by the +`parser` to properly interpret the commands it encounters and turn them into +`Atom[]`. + +An `Atom` is an elementary layout unit, for example a `genfrac` Atom can layout +a "generalized fraction", that is something with a numerator and denominator, +optionally a bar separating the two, and optionally some opening and closing +fences. It is used by the `\frac` command, but also `\choose`, `\pdiff` +and others. + +The `Atom[]` are then turned into `Box[]` which are virtual markup elements. + +Eventually, the `Box[]` get rendered into HTML/SVG markup. + +![Core Architecture](./documentation/assets/mathlive-core.png) + +### Lexer + +The **lexer** converts a string of TeX code into tokens that can be digested by +the parser. + +### Parser + +The **parser** turns a stream of tokens generated by the lexer into **math +atoms**. Those atoms then can be rendered into **boxes**, or back into LaTeX or +into spoken text. + +### Box + +A box is virtual DOM node that is used to represent an element displayed in a +web page: a symbol such as _x_ or _=_, an open brace, a line separating the +numerator and denominator of a fraction, etc... + +The basic layout strategy is to calculate the vertical placement of the boxes +and position them accordingly, while letting the HTML rendering engine position +and display the horizontal items. When horizontal adjustments need to be made, +such as additional space between items the CSS margin are adjusted. + +**boxes** can be rendered to HTML markup with `Box.toMarkup()` before being +displayed on the page. + +### Atom + +An atom is an object representing a mathematical symbol, for example `x`, `1`, a +fraction, a delimiter, etc... + +There are several different classes of Atom (subclass of the base `Atom` class). +Each class represents different layout algorithm (different ways of generating +boxes in their `render()` method) as well as different ways to generate LaTeX to +represent the atom (in their `serialize()` method) + +It can be of one of the following classes: + +- **Atom**: the base class is used for the simplest symbol, e.g. `x`, `1`, + `\alpha` +- **AccentAtom**: a diacritic mark above a symbol +- **ArrayAtom**: "environments" in TeX parlance, a matrix, vector or other + array-like structure +- **BoxAtom**: a decoration around a "nucleus", including a color background, + lines, etc... +- **DelimAtom** and **SizedDelimAtom** delimiters and extensible delimiters +- see `src/atoms` for more. + +### Editor + +The `mathfield` is the object handling the user interaction and driving the +rendering of the formula into the document. + +It makes use of several subcomponents (`virtual-keyboard`, `undo`, etc...) to +handle specific aspects of the user interaction. It makes changes to the formula +by issuing basic editing commands such as `insert()`, `delete()` and modifying +the selection to the `model`. + +The `model` keep track of the state of the formula, including its content (a +tree of `Atom`) and the selection and interacts with the core to turn the `Atom` +into `Box` and into markup. + +![Editor Architecture](./documentation/assets/mathlive-editor.png) + +### Model + +The `Model` class encapsulates the operations that can be done to a tree of +atoms, including adding and removing content and keeping track of and modifying +an insertion point and selection. + +### Mathfield + +The `Mathfield` class is a user interface element that captures the keyboard and +pointing device events, and presents an appropriate user experience. + +It uses a `Model` to manipulate the in-memory representation of the math +expression being edited. ## Publish to npm diff --git a/README.md b/README.md index 5672b0c22..bf7df79f7 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,79 @@
- math live + MathLive
-

Kedyou's fork of MathLive

-

A Web Component for Math Input

+

Kedyou's fork of MathLive

+

MathLive

+

Web components for math input, display, and accessibility.

[![Maintenance](https://img.shields.io/maintenance/yes/2025.svg)]() [![GitHub license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://raw.githubusercontent.com/arnog/mathlive/master/LICENSE.txt) -The MathLive library includes a `` web component that provides an -easy-to-use interface for editing math. +MathLive ships batteries-included UI components that bring TeX-quality math to +the web. Drop a mathfield on the page, wire up the events you care about, and +MathLive handles rendering, editing, speech, and keyboard UX for you. -With over 800 **built-in LaTeX commands**, mathfields render beautiful, -**TeX-quality typesetting**. +- 800+ built-in LaTeX commands with high-fidelity typesetting +- Mobile-ready virtual keyboards and physical keyboard shortcuts +- Export/import as LaTeX, MathML, ASCIIMath, Typst, or MathJSON +- Screen-reader friendly with math-to-speech and ARIA labels +- Highly customizable UI, themes, macros, commands, and behaviors -Mathfields are designed for **mobile devices** with an extensive set of virtual -keyboards for math input, and are compatible with screen readers, including -custom math-to-speech support for improved accessibility. +MathLive demo screenshot -Mathfields output their content as LaTeX, MathML, ASCIIMath, Typst and MathJSON -formats. - -And the best part? They're easy to customize to your needs! - - - - +
- The popover panel - A Virtual Keyboard
- The Loop Equation
-## 🚀 Getting Started +## Components at a Glance + +**``** - The flagship math editor. Provides text-area like APIs +(`value`, `selection`, `executeCommand()`), emits `input` and `change` events, +and exposes a full virtual keyboard UI with custom layouts. + +**``** - Inline, lightweight renderer for static math. Ideal for +embedding expressions inside paragraphs without initializing a full mathfield. -Using MathLive is easy! Simply add a `` tag to your page. It -initializes automatically and works just like a `