Repository navigation
Equation editor for Avalonia: MathEditBox, MathView, and their packages - #33
Open
simonebortolin wants to merge 35 commits into
Open
simonebortolin wants to merge 35 commits into
simonebortolin wants to merge 35 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
A real-time equation editor in the manner of the RichEditBox of WinUI in math mode (math only): UnicodeMath or LaTeX typed at the caret builds up when a space or an operator ends an argument, and the equation is read and written as MathML (GetMath/SetMath) or as an Equation. Three projects, the core unchanged: - SharpEquations.Editing (netstandard2.0, net10.0, no UI): the tree of MathNodes with a row in every slot and runs of linear text, the caret, the selection, undo, build up (with the nodes before the run written back as typed, or as placeholders) and build down to linear text. - SharpEquations.Rendering (netstandard2.0, net10.0, no UI): the layout of MathML Core over the OpenType MATH table, with the ink boxes read from the glyf or CFF outlines of the font, and the geometry of caret and selection. - SharpEquations.Avalonia (net8.0, net10.0): MathEditBox, MathView, the fonts (Cambria Math on Windows, the embedded Libertinus Math elsewhere, OFL). With a demo window, the tests of the three (the controls in a headless session with Skia), CI for them, and docs/EDITOR.md updated to the design as built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
…kages
- Copy: GetSelectedText in any format (UnicodeMath, LaTeX, MathML, OMML),
CopyTextFormat (MathML pastes as an equation into Word), the Copying event.
- Equation numbers: #(1) or \tag{1} typed, or EquationNumber; the number at
the right edge of the line (MathLayoutOptions.LineWidth, side="left").
- Commands: InsertStructure, InsertFences, InsertMatrix, InsertTemplate, Tab
through the cells of a table, UndoLimit (the history forgets the oldest).
- Accessibility: MathEditBoxAutomationPeer, the equation as UnicodeMath.
- LaTeX typing: a control word followed by a space waits for its argument,
and words of LaTeX are written back as \operatorname (86% to 90% of the
latex2mathml corpus typed a character at a time).
- Tests on the other corpora of the repository (latex2mathml, the OMML of
Word documents, mml2tex, MTEF), of undo and delete, of the demo window.
- A demo with the tools of the equations of Word.
- CI packs and publishes the three packages of the editor with the core;
their public API is tracked with PublicApiAnalyzers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
…s, script variants, floating bar - Core: EquationCommand and EquationCommands (public, unshipped): the control words of UnicodeMath and the commands of LaTeX with their symbols, checked against the parsers by PublicApi/EquationCommandsTests. - Editor: completion of commands (CommandPrefix, GetCompletions, Complete); lines (InsertLineBreak, Shift+Enter: equation arrays aligned at &, joined by Backspace and Delete, built up as lines of an array); rows and columns of tables (InsertTableRow, InsertTableColumn, DeleteTableRow, DeleteTableColumn). - Rendering: alignment marks (malignmark) in tables, equation arrays in display style; the ssty variants of the GSUB table in scripts (ScriptStyles); cmap and advances read from the font file. - Avalonia: the floating bar (MathQuickBar) with completions, structures for the selection, table commands and quick symbols; the glyph typeface asked for at each drawing and the embedded font registered with the current font manager, so that no released typeface is drawn with. - Demo and docs updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
A display equation wider than its line (MathLayoutOptions.LineWidth, the width of the control) breaks before its binary operators and relations, the last one that fits each line, as Word does, with the lines after the first indented (BreakLines = false keeps one line). The row stays one row for the editor: the geometry has a line for each caret position, so that the caret, the selection (a rectangle a line), clicks and Up and Down (IMathGeometry.GetPositionOnAdjacentLine) follow the lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
The floating bar took a light background of its own and the foreground of the theme, light in the dark theme: its text could not be read. It now takes the colours of the flyouts of the application's theme (Fluent, Simple), or a light or a dark set by the theme variant, and follows a change of variant; the background of text that does not parse (merror) is dark in the dark theme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
…F-8 bytes Word writes the "MathML" clipboard format as UTF-8 bytes; read as a string of Windows, as UTF-16, each pair of bytes became a CJK character, and the MathML that did not parse was inserted as text, before the plain text of the clipboard. The format is now read and written as bytes (MathClipboard: UTF-8, or UTF-16 with its byte order mark), and MathML or OMML that does not parse inserts nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
samples/SharpEquations.Avalonia.Browser.Demo runs the demo in the browser with Avalonia.Browser: Inter for the text and the embedded Libertinus Math as its fallback, System.Private.Xml kept whole for the OMML stylesheets, a splash with the download progress. The desktop window and the page share DemoView, now redesigned: a header, cards, a ribbon of structures drawn as formulas, symbols by group, a gallery of examples rendered with MathView, the outputs with a copy button, the light and the dark theme, and a layout for narrow screens. The project needs the wasm-tools workload, so it is not in the solution: .github/workflows/demo.yml builds it and publishes it to GitHub Pages from main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVio7wKz4nEifoqJzu9wA
The default math font is Libertinus Math everywhere but Windows, so "Default" and "Libertinus Math" were the same font, and Cambria Math was offered where it is not installed: the change of font showed nothing. The demo embeds STIX Two Math (SIL Open Font License), names the default font, offers Cambria Math only where it is a math font, and applies the font to the gallery too. DemoTests.FontChangesTheLayout checks it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVio7wKz4nEifoqJzu9wA
…efault font to another and back Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVio7wKz4nEifoqJzu9wA
# Conflicts: # NOTICE
… new structures of LaTeX in the commands After the rebase on main, the LaTeX reader reads the arguments of a command without braces (\underset ab, \xrightarrow[y] x, \boxed \Box, \not a): the editor no longer builds up at a space while a command still awaits its arguments, counted from EquationCommands. The commands list the structures the reader now reads: the extensible arrows of amsmath and mathtools, the arrows under a base, \dddot, \ddddot, \mathscr, \not, \substack, \prescript and \ce, \pu of mhchem. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVioB6ZwExLPtzuBzNTgX
…to claude/focused-clarke-f4k4a9
…with, by name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVio7wKz4nEifoqJzu9wA
…to claude/focused-clarke-f4k4a9
…e web demo ctop.xsl, which came from main after the branch, is compiled by the generator too (CtopStylesheet). The stylesheets now count code points as XPath does, not UTF-16 units as XslCompiledTransform did: an emoji (🍕) is an identifier of one letter, written italic in OMML in one run with its neighbours, and goes through OMML and back as the same equation; OmmlAstralTests pins that now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVio7wKz4nEifoqJzu9wA
…h the MATH table Cambria and Cambria Math share their glyphs in cambria.ttc, so the number of glyphs matched Cambria, the first face, which has no MATH table: Cambria Math was never a math font, the default font of Windows was Libertinus Math, and choosing Cambria Math changed nothing. Of the faces with the number of glyphs, FontFile now reads the one with a MATH table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVio7wKz4nEifoqJzu9wA
…ound for XslCompiledTransform With the stylesheets compiled to C#, nothing generates code at run time: the release build of the web demo is compiled to WebAssembly ahead of time, and is trimmed with no warning, without keeping System.Private.Xml whole. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVio7wKz4nEifoqJzu9wA
…hen run by hand Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVio7wKz4nEifoqJzu9wA
Right click, a long press on touch, the Menu key or Shift+F10 open a context menu: undo, redo, cut, copy, paste, copy as MathML/UnicodeMath/LaTeX/OMML, select all, linear/professional, build up/down, new line, equation number and the rows and columns of a table. A right click outside the selection moves the caret first; a read-only editor has only the copy commands and select all. Apps change it with ContextMenuOpening or CreateContextMenu, or turn it off with IsContextMenuEnabled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
…f MathEditBox Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
…onebortolin/SharpEquations into claude/focused-clarke-f4k4a9
MathAutoCorrect replaces \alpha and a space by α (the commands of EquationCommands with a symbol) and the sequences of Word (-> → ≤ ≠ ± …) as they are typed, with a list of entries the application changes; MathEditor.AutoCorrect, and MathEditBox.AutoCorrect and IsAutoCorrectEnabled. An operator that starts a sequence waits for the next character to build up, and an undo right after a replacement gives back what was typed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
… the editor Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
…at each keystroke - MathLayoutCache (Rendering, public): the boxes of the nodes laid out, by node reference, style and stretch, with the paths of their rows relative to the node (anchored where the node is), so that after an edit only the nodes it replaced are laid out again; the glyphs, advances and ink boxes of the font asked of it once (GlyphMetrics). MathLayoutEngine.Layout(root, font, options, cache). - MathEditBox lays out again only when the tree, the font, the size or the width changed, redraws the equation only when the tree or the selection changed, and draws the caret in a layer of its own, which alone is drawn again when it blinks or moves. MathView keeps the tree of its equation and a cache. - MathLayout finds the rows by path without building strings. - Benchmarks of the editor (EditorBenchmarks), and LayoutCacheTests: the layout with the cache is the layout without it, glyph for glyph and caret for caret, on the corpus, typing, and edits inside structures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
…onia # Conflicts: # tests/SharpEquations.Tests/Omml/OmmlAstralTests.cs
…esheet The Browser demo, in the solution since 9894ade, needs the wasm-tools workload, which the test and CodeQL jobs do not install (NETSDK1147 on Linux and macOS): it stays in the solution, built by demo.yml only. The merge of claude/xslt-generator declared CtopStylesheet a second time; it is declared once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVioBuiMiSScbUYtdXLXq
…emoji bitmaps) has the line's box The layout read the ink box of a glyph from the outlines of its font; a font with bitmaps alone (Noto Color Emoji as CBDT, the one of the Linux runners) has none, so an emoji was 0 high and the corpus test failed there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVio8afJsGwHW9i5v5HDs
|
Pre-release NuGet package of 2e11345. Updated on every push, deleted when this PR is closed. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8QQiaNyPsgRSRubdbT58H
…onebortolin/SharpEquations into claude/focused-clarke-f4k4a9
…en they differ Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVioBuiMiSScbUYtdXLXq
…ed once The font Avalonia matches to a code point (TryMatchCharacter) can change with the fonts it has loaded since: the layout without a cache, which asked again at every layout, drew some characters (│, ┬, ▭, ᅲ, emoji) with another font than the one the layout cache kept, and LayoutCacheTests failed on the Linux and macOS runners. The first font matched to a code point is kept. The offset of the cmap subtable is no longer -1 while it is being looked for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyVioBuiMiSScbUYtdXLXq
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A real-time equation editor for Avalonia in the manner of the
RichEditBoxof WinUI in math mode (math only): UnicodeMath or LaTeX typed at the caret builds up into the equation when a space or an operator ends an argument, and the equation is read and written as MathML (GetMath/SetMath) or as anEquation. The design is indocs/EDITOR.md.Projects
SharpEquations.EditingMathNodetree with runs of linear text, caret, selection, undo (a bounded stack of immutable trees), build up and build downSharpEquations.Renderingglyf/CFF outlines,sstyvariants from GSUB; caret and selection geometrySharpEquations.AvaloniaMathEditBox,MathView, the floating bar (MathQuickBar); Cambria Math on Windows, embedded Libertinus Math (OFL) elsewheresamples/SharpEquations.Avalonia.DemoFeatures
GetSelectedText,CopyTextFormat: MathML pastes into Word,Copyingevent), paste of MathML/UnicodeMath/LaTeX#(1),\tag{1},EquationNumber)&; automatic line breaks of long display equations before the operatorsMathEditBoxAutomationPeer, the equation as UnicodeMathCore
The core's only change is new public API:
EquationCommandandEquationCommands(the control words of UnicodeMath and the commands of LaTeX with their symbols), inPublicAPI.Unshipped.txt, checked against the parsers byPublicApi/EquationCommandsTests. It calls for a minor version:VersionPrefixis still 1.0.5 and the commits say[no-new-version], so the bump is to be made before merging.Release
The CI tests the editor (
tests/SharpEquations.Avalonia.Tests, net10.0, and net8.0 on Linux), and packs and publishes the three packages of the editor with the core, with one version. Their public API is tracked inPublicAPI.*.txt. The package IDsSharpEquations.Editing,.Renderingand.Avaloniaare new on nuget.org: the Trusted Publishing policy ofNUGET_USERhas to cover them.Tests
dotnet test --solution SharpEquations.slnx: 5673 passed. No test was run on Windows or macOS, or with a real window.Generated by Claude Code