mathtex-editor-core 0.3.0

Headless core of the mathtex structural math editor: model, operations, navigation, selection, IR matching
Documentation

mathtex-editor

Structural math editing for Rust and the web, typeset by real TeX.

Type x^2, a/b or alpha and watch it lay out the way TeX would. The document is a tree, so the caret moves through fractions, scripts and matrices the way you think about them, and every edit exports clean LaTeX.

  • Real TeX layout. Every keystroke is typeset by mathtex, the XeTeX engine in Rust, in the browser or natively.
  • Keyboard first. Autocorrect for <= and ->, about 190 backslash words, a command palette with live previews, and a Backspace that selects a structure first and offers to swap brackets.
  • Pick your layer. An explicit headless core, a pluggable keymap, a batteries included session, a wasm runtime and a Leptos component. Take as much or as little as your app needs.
  • Your widgets inside the math. Host boxes reserve space in a formula for anything you draw, from a text field to a chart.
  • Documents you can trust. Versioned JSON, validated on load, with seeded fuzz tests over random edits.

Crates

Crate Role
mathtex-editor-core the editable tree, caret, selection, LaTeX export and geometry, with no hidden behaviour
mathtex-editor-keymap key events and typed text to editor commands
mathtex-editor-session core, keymap and typesetter wired together with undo, clipboard and host box tokens
mathtex-editor-js the session for the web, rendering SVG
mathtex-editor-leptos a MathEditor component for Leptos 0.8

Quick start

let mut session = Session::new(typesetter);
session.key(&KeyInput { key: "x".into(), shift: false, ctrl: false, alt: false, meta: false });
let view = session.view()?;
// Draw view.fragment, then view.render.caret, selection and placeholders on top.

Build the typesetter from a baked mathtex format as the mathtex README shows. The session typesets only when the document changed, keeps undo in one step per edit, copies as JSON and LaTeX, and gives pasted host boxes fresh tokens. UndoStack, TokenRegistry and RenderCache also work on their own.

Full control with the core

The core never typesets, keeps no history, reads no keys and calls nothing back. You send a command and get a value back that says exactly what happened.

let before = editor.snapshot();
if editor.exec(Command::InsertText("x".into())).changed {
    history.push(before);
}
let source = editor.source();
let output = editor.render(&source, &typeset(&source.tex))?;
  • exec returns an Outcome with changed, moved, exit, close, entered_host_box and the revision. Retypeset only when the revision moves.
  • A CaretPath names the route to the caret through each slot, survives serialization and can be set directly with set_cursor or set_selection.
  • snapshot and restore capture the document with its caret for undo.
  • at_start, in_text_slot, menu, matrix_shape and input_context answer questions without moving anything.
  • Geometry is in points with y down from the fragment's top left, the units of the mathtex SVG viewBox, and source spans are byte ranges of the LaTeX.

Typing

The keymap turns a/b into a fraction, x^2 into a superscript and alpha plus space into α. The longest known word at the end wins, so xsin gives x then sin. <=, -> and friends become one symbol, while ordinary input like x<-1 is left alone. Inside \text{} everything types literally.

Read Editor::input_context() before each map_key or map_text call, run the returned commands once in order, and call reset() when the caret moves outside the keymap. Conversions ask the core to replace exactly the letters typed, so a stale word can never delete the wrong thing.

Documents

Document is versioned JSON, the unit of storage and of the clipboard.

{"version":1,"root":[{"type":"atom","data":{"latex":"x","class":"ord"}}]}

Every document is validated on load, Document::repair fixes one built in code and lists each fix, and to_tex_with exports LaTeX with your own content in place of host boxes. The LaTeX uses amsmath, and \mathbb or \mathfrak need amssymb or a Unicode math font.

On the web

const editor = new WebEditor(formatBytes, (name, kind) => fonts.get(name) ?? null);
editor.key('x', false, false, false, false);
stage.innerHTML = editor.render();

The resolver hands back font files as Uint8Array or null. key, text, click, the clipboard calls and the toolbar calls return an Update saying what changed. render returns SVG with the caret and selection drawn in and throws the TeX error when a formula does not typeset. The demo in web/ shows the whole feature set, and web/hostbox.html puts live HTML inputs inside a formula.

In Leptos, MathEditor takes a shared WebEditor and handles keys, IME, pointer, clipboard, undo and the swap menu for you.

Development

cargo test --workspace
wasm-pack build crates/mathtex-editor-js --target nodejs --out-dir pkg-node --release
node test/e2e.mjs

The e2e reads format.pkg and fonts from MATHTEX_ASSETS, the sibling mathtex checkout's web/assets by default. For the web demo, build with --target web, copy the output with cp -r crates/mathtex-editor-js/pkg/. web/pkg/ and the assets into web/assets, then serve web/.

Known gaps

  • Up and Down between unrelated lines only moves structurally, through fractions, scripts and matrices.
  • The Leptos component compiles for wasm32 but no browser test drives it yet.

Licensed under either Apache-2.0 or MIT.