Skip to content

Visual equation editor: OMML equations, 2-D layout, in-place typing - #55

Open
shuff57 wants to merge 8 commits into
storytold:mainfrom
shuff57:equation-editor
Open

shuff57 wants to merge 8 commits into
storytold:mainfrom
shuff57:equation-editor

Conversation

@shuff57

@shuff57 shuff57 commented Oct 10, 2026

Copy link
Copy Markdown

Visual equation editor

Insert → Equation used to be a one-line text field that swapped a few tokens for Unicode and inserted an italic text box. This replaces it with a real equation object and an editor where you type into the formatted math (x, ^, 2 shows a raised exponent immediately).

What it adds

  • deckcraft-math (new layer-1 crate): math tree, OMML reader/writer (unknown elements preserved as raw XML, bounded against hostile input), linear-text parser/exporter, template catalogue, and a caret/selection editing model.
  • Commands: insert.equation, equation.update, equation.get, equation.templates, equation.fromLinear, equation.type. Equations are stored as RunKind::Math OMML and round-trip through PPTX. The importer now sets run text via to_linear(from_omml) and keeps run properties.
  • Layout: TeX-style 2-D layout in deckcraft-text (fractions, roots, scripts, big operators with limits, delimiters, matrices); Math runs are inline atoms that keep character/caret counts.
  • Editor dialog: template palette, live preview, in-place typing with caret and selection (^ _ /, sqrt, Greek names, symbol shortcuts, auto-paired brackets), undo/redo, copy/cut, Tab between places. Double-click on an equation reopens it. The linear text field remains as a secondary path.

Known limitations

  • Radical and bracket fit is rougher than KaTeX; the real fix is a math-capable OFL font (STIX Two / Latin Modern Math) via craft-fonts. Noted in ROADMAP.md.
  • OMML that rebinds the m prefix to a non-OMML URI is mis-namespaced on rewrite (documented in code; Office does not produce this).
  • Editing happens in the dialog preview, not directly on the slide.
  • Not tested against real PowerPoint; OMML shapes follow the public ECMA-376 spec and hand-written samples only.

Verification

cargo xtask ci (fmt, clippy, tests, assets, layers, wasm) passed on this branch. No Autodesk/Microsoft/Adobe assets or third-party math-editor code were used; MathLive and KaTeX were only observed in a browser as comparison references.

Opened as a draft: this is a large change (about 40 commits) and I'm happy to squash or split it however you prefer.

🤖 Generated with Claude Code

@echelon

echelon commented Oct 10, 2026

Copy link
Copy Markdown
Member

Thank you, @shuff57, this is impressive work: a clean-room OMML editor with about 196 tests, no new external crates and no bundled fonts.

It can't merge yet: #4 (UAX #9 bidi + Arabic shaping) landed and rewrote the paragraph cell model in crates/text/src/lib.rs (cells now carry level/glyphs/head/boundary and are built from the logical text). The equation atoms need to be re-slotted into that model, not mechanically merged. There are also small conflicts in text/tests.rs, engine/tools.rs, render/lib.rs, pptx/tests/roundtrip.rs and Cargo.lock.

Could you rebase onto main, add a test with an equation inside a right-to-left (Arabic) paragraph, and get cargo xtask ci green? We'd love to land it then.

If you're not on our Discord yet, come join us: https://discord.gg/artcraft

@shuff57
shuff57 marked this pull request as ready for review October 10, 2026 15:56
@shuff57

shuff57 commented Oct 10, 2026

Copy link
Copy Markdown
Author

Thanks! Rebased onto current main (d556ff9) and squashed to 4 commits.

Bidi integration. The equation atoms are re-slotted into the new paragraph cell model rather than merged mechanically: an equation is one U+FFFC object (a bidi neutral) followed by zero-width U+2060 joiners, one per remaining character of the run. It takes part in bidi and line breaking but is kept out of font shaping, only its first character is a caret stop or break point, and it is placed from the per-cell visual edges, so it lands correctly at any embedding level. The equation box itself is drawn whole and never mirrored. Glyph runs set cells so the morph transition treats an equation as one character.

Tests added (crates/text/src/tests.rs), run against a real Arabic face:

  • equation_in_arabic_paragraph_follows_reading_order: Arabic word, equation, Arabic word in an RTL paragraph (reading order, box not mirrored, caret edges, char counts)
  • equation_alone_in_an_rtl_paragraph_sits_at_the_start_edge
  • equation_between_latin_and_arabic_keeps_logical_neighbours

cargo xtask ci is green (fmt, clippy, test, assets, layers, wasm). I also rendered RTL paragraphs with mid-line, end-of-line, wrapped, justified (kashida) and highlighted equations and checked them by eye, and double-clicked an equation in an RTL paragraph in the app to confirm the editor opens and updates it in place.

shuff57 and others added 8 commits October 10, 2026 20:01
OMML tree, linear (LaTeX-ish) format, templates, bounded namespace handling and
lossless round-trip of PowerPoint equations; pptx reader/writer keep Math runs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
insert.equation, equation.update/get/templates/fromLinear with strict param
validation; Math runs are atomic for copy/cut/format and whole-run deletion.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Fractions, roots, scripts, big operators with limits, delimiters and matrices,
laid out as inline atoms that keep caret and character counts; drawn vector
shapes for radicals, brackets and sum/product signs (Deco carries a path).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Palette dialog with live preview, a math editor model (caret, selection,
structure typing, undo/redo, clipboard) and MathLive-style typing grammar.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…L, delimited LaTeX)

edit.paste recognises clipboard text that is exactly one equation payload:
an m:oMath / m:oMathPara (optional XML declaration), a presentation MathML
<math> element, or LaTeX in $$..$$ / \[..\] (display) or $..$ / \(..\)
(inline) delimiters spanning the whole text. It inserts a real equation in
one undo step; markup that does not parse is pasted as text, as before,
with a one-line status note. Keep Text Only / Paste and Match Formatting
still paste the literal text.

math crate: xml::well_formed (strict check before trusting the lenient
reader), clip.rs (detection), mathml.rs (bounded presentation-MathML
reader; a parallel implementation, equaSHion has no MathML reader yet).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…on; \bar macron is an accent

A pasted HTML clipboard fragment (<!--StartFragment--> or <body>) whose
only content is one <math> or <m:oMath> element reads like the bare
element. MathML over-marks U+0305 and the macron become the \bar accent,
matching equaSHion's to_mathml; checked against equaSHion's writer (19 of
19 sample equations read to the same linear form as its OMML).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…re ROADMAP)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants