Upleft is a Rust rewrite of Downright, the native macOS Markdown reader and editor. It uses AppKit and TextKit 2 through objc2.
The goal is strict: the same output as Downright, pixel for pixel, at the same speed or faster. Every layer is checked against the Swift original, which is built from source out of vendor/downright.
The layers below are ported in this order. A layer counts as done only when the harness agrees with the original on it.
| Layer | Swift source | Rust crate | Conformance gate | Status |
|---|---|---|---|---|
| cmark-gfm (C) → pulldown-cmark | swiftlang/swift-cmark @ 7898f1b |
Upleft's fork of pulldown-cmark 0.13.4 (vendor/pulldown-cmark) plus the adapter in upleft-markup; upleft-cmark-gfm-sys (the C sources) is now only a test oracle |
syntax-tree dump identical | replaced: no shipped binary links C Markdown code. The fork's ENABLE_CMARK_GFM_COMPAT option parses as cmark-gfm does wherever CommonMark 0.31 and cmark-gfm disagree (the fork's UPLEFT.md lists each case), and the adapter rebuilds cmark-gfm's tree and source ranges, quirks included. Against the C parser it is identical on 251/251 corpus documents, 744/744 spec examples, 7960/7960 corpus documents after the incremental suite's edits, and every seeded mutated, random and line-built document tried, several million in all. No difference is known; see docs/KNOWN-DIFFERENCES.md. |
| swift-markdown converter | apple/swift-markdown @ 27b7fc1 |
upleft-markup |
syntax-tree dump identical | done: markup 915/915 corpus documents identical; the 5,000-line document parses in 0.9 ms vs 10.6 ms in Swift (1.5 ms with the old cmark-gfm converter) |
| MarkdownCore | Sources/MarkdownCore |
upleft-core |
parse dump identical |
done: parse 915/915 corpus documents identical (plus 14/14 UTF-8 edge cases); the text-level functions (core-text, core-io) are identical on 915/915 documents and 33/33 byte-level edge cases. The 5,000-line document parses in 3.9 ms vs 24.1 ms in Swift, and every other benchmarked stage is at least as fast. |
| MarkdownRender | Sources/MarkdownRender |
upleft-render |
decorate dump and render pixels identical |
done. The text view, every object fragment (lists, code, callouts, tables, images, rules, front matter, math, Mermaid), the density gutter and the decoration engine: render 3660/3660 (every corpus document in 4 variants), render-state 100/100 (all themes, Read mode, below the fold, highlights, change marks, folding, zoom, streamed appends, edits), render-dark-themes 100/100, render-images 28/28, decorate 5490/5490, incremental 2745/2745, displaymap 2745/2745. All captures are headless. Wholesale decoration is 0.44× Swift's time and document open to first frame 0.79×. See docs/VALIDATION.md. |
| SwiftMath | Vendor/SwiftMath |
upleft-math |
math pixels identical | done: math-image and math-tree each 15420/15420 identical (1285 inputs × 6 themes × light and dark); parse, typeset and render together take 137–175 ms vs 263–278 ms in Swift |
| beautiful-mermaid + ELK | lukilabs/* |
upleft-mermaid, upleft-elk |
diagram pixels identical | done. ELK: elk 449/449 layouts identical (Mermaid-generated, elk-swift test, random and polyline graphs; where elk-swift itself varies between runs, Rust matches one of its outcomes), 6.6–14× faster than elk-swift. Mermaid, on 308 corpus diagrams of every type: mermaid-parse 308/308, mermaid-layout 1232/1232 and mermaid (bridge pixels) 1232/1232 across light, dark, Nord and High Contrast; mermaid-replay 211/211 (ELK inputs byte-identical to Swift's). The uncached bridge path is about 2.7× faster than Swift. |
| DownrightApp and the command-line tools | Sources/DownrightApp, drdownright, down, DownrightSpotlightMetadata, DownrightSpotlightImporter, DownrightQL, DownrightThumb |
upleft-app, upleft (the app binary), upleft-cli, upleft-spotlight-metadata, upleft-spotlight-importer, upleft-quicklook, upleft-thumb, upleft-foundation |
app-layer suites identical; window captures identical | in progress. Done: the non-UI layer, the command-line tools, the windows, menus and panels, the app binary and just upleft-app (Upleft.app with down, Sparkle 2.9.6, the Spotlight importer and both Quick Look extensions). html-export 14042/14042 (6 themes × light and dark, plus print), spotlight 928/928, down-cli 244/244, workspace 6/6, find 4/4, palette 23/23, formats 30/30, updater 10/10, local-ai 4/4; app-window 42/42 (document window, start, setup and Settings windows, captured off-screen by the window server) and app-menu 4/4; panel 228/228 and panel-model 39/39 (589 states: every Panels/ view built from a scenario off-screen, window-server pixels and view-tree dumps); quicklook-preview 16/16, quicklook-thumbnail 2745/2745. All just app-bench and just panel-bench stages (task panel on the 5,000-line document 0.76×, palette ranking 0.74–0.77× Swift) and just app-window-bench (document open, mode switch: 0.61–0.89× Swift) are as fast or faster. Unverified: the app launched on screen, a real Sparkle update, the system loading the importer and extensions. See crates/app/PORTING.md and crates/app/src/panels/PORTING.md. |
just bench runs Downright's own drbench and Upleft's upleft-bench three times each, interleaved, and compares the median p50 of every stage. It fails if any stage is slower than Swift by more than 5% (a run-to-run noise allowance). Latest run on an Apple Silicon MacBook Pro, milliseconds:
| Stage | Swift p50 | Upleft p50 | Upleft / Swift |
|---|---|---|---|
| cmark alone (5k lines; Upleft: pulldown-cmark and the adapter) | 10.6 | 0.89 | 0.08 |
| MarkdownParser.parse, all passes | 24.1 | 3.9 | 0.16 |
| parse 100 KB (cold open) | 19.8 | 3.1 | 0.16 |
| incremental decorate, one dirty block | 0.086 | 0.045 | 0.52 |
| wholesale decorate (mode switch) | 93.2 | 41.1 | 0.44 |
| edit + paragraph map (typing response) | 0.139 | 0.076 | 0.55 |
| worker pipeline (end-to-end convergence) | 24.1 | 3.9 | 0.16 |
| Metrics.metrics | 22.9 | 4.8 | 0.21 |
| syntax highlight 10 KB | 0.082 | 0.046 | 0.56 |
All 21 drbench stages are as fast or faster. The math, Mermaid and ELK layers have their own benchmarks; see each crate's PORTING.md and the status table above.
vendor/downright the Swift original (git submodule, pinned)
vendor/swift-cmark cmark-gfm at the revision Downright resolves (Upleft's test oracle)
vendor/pulldown-cmark Upleft's fork of pulldown-cmark, the parser (see its UPLEFT.md)
vendor/swift-markdown swift-markdown at the revision Downright resolves
vendor/beautiful-mermaid-swift, vendor/elk-swift Mermaid dependencies
oracle/ downright-oracle: the Swift reference for conformance
crates/ the Rust port
crates/swift-text Swift String/Character/CharacterSet/NSString semantics shared by
every crate, with Unicode tables generated from the Swift runtime
and checked for every scalar by the `unicode` suite
corpus/ documents the conformance runner checks
Other applications can embed the renderer: docs/EMBEDDING.md covers hosted mode, where each MarkdownTextView is one message in the host's own scroll view (a chat transcript, for example).
oracle/ is a small Swift package that links Downright's own MarkdownCore and MarkdownRender. It writes three outputs for a document:
parsedumps the parsed document tree: every block, inline span, range, hash, and derived structure.decoratedumps every attribute run on the decoratedNSTextStorage. Fonts, colors, and paragraph styles are compared bit for bit.rendercaptures a PNG of the realMarkdownContainerView, along with a dump of the layout fragments. It runs headless by default: the app is never activated, its window sits off-screen, andcacheDisplayrecords it. An on-screen ScreenCaptureKit capture (--capture screen) is opt-in, for a handful of spot checks.
upleft-oracle takes the same arguments and writes the same formats. The runner compares the two outputs structurally and decodes the PNGs to compare pixels exactly. A render request can also be sent to the full app. Downright's DOWNRIGHT_DEBUG_LAYOUT and DOWNRIGHT_DEBUG_CAPTURE hooks capture the whole window, and Upleft implements the same hooks.
git submodule update --init
just oracle # build downright-oracle
just conform # run the corpus through both and compareUpleft is MIT-licensed and keeps Downright's copyright notice. Ports of third-party code keep their upstream license:
upleft-markup(from swift-markdown) is Apache-2.0.upleft-elk(from elk-swift) is EPL-2.0.upleft-math(from SwiftMath) andupleft-mermaid(from beautiful-mermaid-swift) are MIT.