1. Docs
  2. Developers
  3. Editor systems

LaTeX engine

The custom math stack: lexer, parser, box layout, and Avalonia rendering.

Mnemo renders math with its own engine. It is not MathJax, KaTeX, or a web view: a hand-written lexer and parser produce an AST, a layout pass turns the AST into a tree of measured boxes, and the boxes draw themselves onto an Avalonia drawing context. The engine exists so equations render offline, instantly, inside native controls, and in PDF export, with one code path for all of them.

Pipeline

flowchart LR
  SRC["LaTeX string"] --> LEX[LaTeXLexer]
  LEX --> PAR["LaTeXParser<br/>AST of LaTeXNode"]
  PAR --> LAY["LayoutBuilder<br/>box tree"]
  LAY --> REN["Box.Render<br/>MathRenderContext"]
  REN --> CTL["LaTeXRenderer control<br/>editor, chat, PDF"]

The split across projects follows the dependency rule: lexer, parser, and the symbol table are pure and live in Mnemo.Infrastructure/Services/LaTeX/; layout and rendering need font measurement and Avalonia types, so they live in Mnemo.UI/Services/LaTeX/. LaTeXEngine (Mnemo.UI/Services/LaTeXEngine.cs, implementing ILaTeXEngine from Core) orchestrates the stages and owns the caches.

Parsing

LaTeXParser produces these node types (Mnemo.Infrastructure/Services/LaTeX/Parser/Nodes/): TextNode, SymbolNode, FractionNode, ScriptNode, GroupNode, SqrtNode, DelimiterNode, SpaceNode, TextModeNode, MathbbNode, MathbfNode, and MatrixNode.

Recognized constructs:

ConstructHandling
\frac{a}{b}FractionNode
\sqrt{x}, \sqrt[n]{x}SqrtNode
_ and ^ScriptNode, combined sub plus super supported
\left ... \rightDelimiterNode with scaled delimiters
\begin{matrix} family (matrix, pmatrix, bmatrix, vmatrix, Vmatrix)MatrixNode; \\ breaks rows, & separates columns
\text{...}text mode
\mathbb{...}, \mathbf{...}mapped to Unicode math alphanumeric symbols
\quad, \qquadfixed horizontal space
\displaystyle, \textstyleaccepted, currently no-ops
any other \commandlooked up in the symbol registry

SymbolRegistry (.../Symbols/SymbolRegistry.cs) maps roughly 200 commands to Unicode: Greek letters, operators, relations, arrows, set and logic symbols, delimiters, and miscellany. An unknown command does not fail the parse; it falls through to a SymbolNode that renders the command name as literal text. Parse errors are collected into a list and the parser keeps going, so partial input still renders.

Other \begin{...} environments parse but get no special layout; their content renders inline. align, colors, and arbitrary font sizing are not supported.

Layout

LayoutBuilder (Mnemo.UI/Services/LaTeX/Layout/) converts the AST into a box tree: HBox, VBox, CharBox, FractionBox, ScriptBox, SqrtBox, MatrixBox, SpaceBox, RuleBox, and ScaledDelimiterBox. Each box knows its width, height, and baseline. FontMetrics (.../Metrics/FontMetrics.cs) measures characters through Avalonia and caches measurements; spacing rules for fractions, scripts, and delimiters live there too. Layout uses the app’s math font resource (MathFontFamily), not full OpenType math tables, which is the main fidelity limitation versus a TeX engine.

Rendering

Boxes draw through IMathRenderContext, implemented by MathRenderContext (Mnemo.UI/Services/LaTeX/Rendering/) over Avalonia’s DrawingContext. LaTeXRenderer (Mnemo.UI/Controls/LaTeXRenderer.cs) is the reusable control that hosts a rendered equation; it appears in equation blocks, chat markdown, and anywhere else math shows up. PDF export rasterizes through the same engine.

Caching

Every stage caches, because equations re-render constantly while typing:

CacheKeyCapacity
Parse results in LaTeXEngineLaTeX string500
Layout trees in LaTeXEnginestring plus font size1000
Glyph FormattedTextchar, size, color500
Font measurements in FontMetricschar, sizeunbounded concurrent
Inline equation boxesstring plus font sizeper editor, via InlineEquationController

LaTeXEngine.ClearCache() flushes parse, layout, and metric caches; it runs when the math font or theme changes.

Editor integration

Block equations (BlockType.Equation) store LaTeX in EquationPayload and render lazily when scrolled into view (EquationBlockComponent). Inline equations are EquationSpan values inside flow text: InlineEquationController builds and caches their boxes, RichTextLayoutBuilder reserves their width in the text layout, and RichTextEditor.InlineEquations.cs paints them over the text. Clicking either kind opens a small source editor; inline equations behave as a single atomic character for caret and selection purposes.

Where the code lives

ConcernPath
Lexer and parserMnemo.Infrastructure/Services/LaTeX/Parser/
Symbol tableMnemo.Infrastructure/Services/LaTeX/Symbols/SymbolRegistry.cs
Engine facadeMnemo.UI/Services/LaTeXEngine.cs, contract Mnemo.Core/Services/ILateXEngine.cs
Layout and boxesMnemo.UI/Services/LaTeX/Layout/
RenderingMnemo.UI/Services/LaTeX/Rendering/, Mnemo.UI/Controls/LaTeXRenderer.cs
Inline integrationMnemo.UI/Components/BlockEditor/RichText/InlineEquationController.cs