leaf-ffi 0.4.3

C-ABI / Swift frontend binding for leaf: wraps the filesystem-free leaf-core Doc behind UniFFI so an AppKit/SwiftUI app can drive the byte-offset caret model and render the VisualMap as style runs — the native-Apple peer of leaf-wasm.
Documentation

leaf-ffi

The C-ABI / UniFFI Rust binding for leaf: it wraps the filesystem-free leaf-core Doc behind UniFFI so a native Apple app can drive the byte-offset caret model and render the VisualMap as style runs. The native-Apple peer of leaf-wasm.

This crate is only the Rust binding (src/lib.rs + the uniffi-bindgen bin). The Swift side built on top of it lives elsewhere:

Piece Location What it is
Swift SDK packages/leaf-swift Sources/LeafUI (the AppKit/UIKit editor) + the committed uniffi-generated/ Swift, exposed by the Package.swift at the repo root. The importable Swift package.
Demo app apps/leaf-editor The runnable cross-platform (macOS + iOS) example (bootstrap.sh, xcodegen project.yml).

The Swift bindings are (re)generated from this crate by scripts/gen-bindings.sh into packages/leaf-swift/uniffi-generated/, which is committed — the Swift package is consumed by version from a bare git checkout, so the binding must build as-is. CI diffs the committed binding against this crate on every push; regenerate and commit after changing the FFI surface.

How long the calls take

examples/bench.rs times the calls a native frontend makes per interaction — the frame, the offset lookups, a click, a keystroke, the counts — on a generated 14,000-word document or one you name, and prints a table:

cargo run -p leaf-ffi --example bench              # dev profile
cargo run -p leaf-ffi --example bench --release    # what a shipping app sees
cargo run -p leaf-ffi --example bench -- path.md   # your own document
cargo run -p leaf-ffi --example bench -- --dump path.md   # write the generated one out

Run it in both profiles: the dev build is what the app is driven with while developing, and it is ten times slower on exactly the scans the table shows. A row that grows with the document is the finding; the Swift renderer's own per-interaction cost sits above these numbers, not in them. The last rows time the same gestures on a document answering with changes rather than whole frames (set_incremental_frames), with the rows each lifts in the label — the number that must not grow when the document is concatenated with itself (--dump it, then cat two copies and name the result).