Expand description
leaf-ffi — the Swift / C-ABI frontend binding for leaf.
This is the native-Apple analogue of leaf-wasm: it takes leaf-core’s
frontend-neutral Doc — the byte-offset caret model and the AST→glyph
VisualMap — and exposes it across a C ABI (via UniFFI) in the shape an
AppKit/SwiftUI renderer wants. Core stays the single source of truth for the
text, the caret math, and the offset⇄position mapping; the Swift side only
paints glyphs and forwards key/mouse events back in, exactly as the TUI, gpui,
and wasm frontends do.
§The boundary is style runs, not glyphs
Doc::build_visual resolves the document to rows of per-character glyphs,
each tagged with a semantic Role and the author’s emphasis. Sending one
object per character would make every keystroke O(document) in boundary
crossings. Instead LeafDoc::view coalesces each row’s glyphs into maximal
runs of identical style and ships those — a handful of records per line.
The Swift renderer maps each run’s role to a font/size/weight and its
emphasis flags to traits, the native counterpart of the TUI’s to_ratatui
and the web’s CSS class.
§Core owns the grid; Swift owns the pixels
Core lays a row out in whole character columns (a terminal-cell measure),
and every offset⇄position method speaks that grid. It deliberately does not
dictate presentation. So a native renderer is proportional — body text in a
real family, headings by size and weight, code in a monospace panel — and
never multiplies col × cell_width. It lets NSLayoutManager / Core Text
shape each row, places the caret at DocView::caret_ch (a UTF-16 offset,
which is exactly what NSAttributedString and NSTextView count in), and
hit-tests a click through characterIndex(for:), feeding the resulting
row + UTF-16 offset back through LeafDoc::click_ch. Core measures nothing
in pixels; Swift positions nothing in the model.
§Threading
A UniFFI object is handed to Swift as a reference-counted handle whose methods
take &self, so the Doc lives behind a Mutex. Every call locks, edits
or reads, and returns a fresh DocView — one boundary crossing both mutates
and repaints, same as the wasm frontend. Drive it from the main thread.
Structs§
- Boundary
- What a drawn block boundary separates. The FFI mirror of
leaf_core::Boundary. - Capabilities
- Which formatting controls this document’s format can spell — the toolbar’s
enabled state, one flag per button, from
LeafDoc::capabilities. Mirrorsleaf_core::Capabilities, where the reasoning lives. - Directive
Attr - One
{key=value}attribute of aDirectiveView. A record rather than a tuple because UniFFI has no tuple type; an absent value flattens to"", since a bare attribute is a flag and the distinction fromkey=""has no consumer on this side. - Directive
View - A leaf directive (
::name{…}) — a standalone block with no body, drawn inDocView::rowsas a one-row⧉ nameplaceholder. A frontend that knows the host app’s vocabulary reads this and paints the real thing over the rows in[start_row, end_row)— a web view for diaryx’s::embed{src=…}, say — exactly as a grid-drawing one replaces aTableView’s picture rows. One that doesn’t just paints the placeholder, which is already framed by the directive panel chrome. - DocView
- A whole rendered frame: the rows to paint, where the caret sits, and the toolbar state — everything the Swift side needs for one repaint, in one value. Returned by every view-producing method.
- Footnote
DefView - A footnote definition and the reference that sends a reader to it — what
LeafDoc::footnote_definition_at_caretanswers with, and the FFI mirror ofleaf_core::FootnoteDef. - Footnote
View - A footnote reference and the note it names — what
LeafDoc::footnote_atanswers with. The FFI mirror ofleaf_core::FootnoteRef. - Landing
View - Where a locator lands — what
LeafDoc::locateanswers with, and the FFI mirror ofleaf_core::Landing. - LeafDoc
- A live leaf document bound for a native Apple frontend:
leaf_core::Docplus the wrap width the current viewport implies, behind a mutex. Constructed from an in-memory string and driven entirely through method calls — there is no filesystem behind it. - Media
Height - A per-destination measured height, the way Swift reports one back — the input
half of the loop
LeafDoc::set_media_rowscloses. - Media
Source View - One
<source>alternative of a block media element — a candidate URL plus whichever of the two things HTML picks a<source>by: a media query (<picture>) or a MIME type (<video>/<audio>). - Media
View - One block-level image, video, or audio: which rows core reserved for it and
what to build there. The peer of
leaf_core::MediaInfo, and the media analogue ofDirectiveView— a frontend skips the rows instart_row..end_rowand lays its own view over them, rather than painting the🖼/🎬/🔊placeholder glyphs core put there for a surface that can’t. - Row
- One visual line: its styled runs plus the row-level flags a frontend draws chrome from.
- RowCol
- A visual position: a row index plus a UTF-16 offset within that row’s text —
the coordinate the geometry side (Core Text) draws from. Returned by
LeafDoc::pos_for_offset, the bridge from a source offset (what aUITextPositionwraps) to where it sits on screen. - RowRange
- The rows a source range covers, both ends inclusive — what a frontend
slices out of a frame to draw a block somewhere other than where it sits: a
footnote peek, a link peek, a landing flash. Returned by
LeafDoc::row_range_for. - Run
- One maximal span of same-styled glyphs on a visual row — the unit the Swift renderer turns into a single styled attributed-string run.
- Table
Cell Line View - One visual line of a table cell: its styled runs and the source offsets
bounding it. A cell is usually one line, but an in-cell hard break (an inline
<br>) splits it into several — each its own line here, so the frontend shapes and caret-maps them independently (the byte↔UTF-16 offset math a cell needs holds within a line, which carries no break). The runs are unwrapped: column width — and any soft wrap within it — is the frontend’s to decide. - Table
Cell View - One cell of a table’s structural grid: its content as one or more visual lines, the column alignment its text honours, and the source range the whole cell occupies (where a click or the caret lands).
- Table
RowView - One row of a table’s structural grid; a header row draws bold and is ruled off from the body below it.
- Table
View - A table described structurally rather than as the monospace box-glyph
picture that spells it in
DocView::rows. A proportional renderer draws its own grid from this — columns sized to content, real borders — and SKIPS the picture rows in[start_row, end_row). The two describe the same cells at the same source offsets, so the caret lands identically either way. Seeleaf_core::TableInfo.
Enums§
- Block
Class - The block kinds core tells apart — the vocabulary a
Boundaryis spelled in. The FFI mirror ofleaf_core::BlockClass;Othercovers every kind core doesn’t separate out, so a frontend’smatchstays exhaustive as the list grows. - Leaf
Error - A parse failure constructing a document — the only fallible entry point. Every
other method is infallible (it operates on an already-parsed model), so they
return a
DocViewdirectly. - Line
Flow - How the rich view treats a soft break (a bare newline inside a paragraph) —
the argument to
LeafDoc::set_line_flow. Mirrorsleaf_core::LineFlow;Foldis the default (soft breaks reflow into the paragraph, as before). - Markup
Mode - How much of the source markup the rich view exposes — the argument to
LeafDoc::set_markup_mode. Mirrorsleaf_core::MarkupMode;Noneis the default (the clean surface Diaryx ships, with typed syntax kept literal). - Media
Kind - What a block-level media placeholder is, so Swift knows which view to build
over the rows core reserved: an
NSImageView/UIImageView, or anAVPlayerViewwith or without a picture to show. The peer ofleaf_core::MediaKind. - Table
Alignment - A table column’s text alignment — the argument to
LeafDoc::table_set_alignment. Mirrors twig’sAlignment.