lexical-yjs-html
Renders a Lexical document to HTML straight from its Yjs form, using yrs. No browser, no Node. It handles core Lexical: paragraphs, headings, quotes, code blocks, lists, tables, links, and text formatting. The tests check output byte-for-byte against documents captured from a real editor.
Usage
[]
= "0.1"
= { = "0.27", = ["sync"] }
Feed it a Yjs update. That is bytes from your store, a provider, or
Y.encodeStateAsUpdate in the browser.
use Decode;
use ;
let update_bytes: = read.unwrap;
let doc = new;
doc.transact_mut
.apply_update
.unwrap;
let txn = doc.transact;
let fragment = txn.get_xml_fragment.expect;
let html = render;
// => Some("<h1>Heading One</h1><p>…</p>") — or None if the fragment
// isn't Lexical-shaped (e.g. a ProseMirror document).
One doc can hold several fragments. Pass the root name your editor binds.
render returns None when the fragment isn't Lexical-shaped.
Declarative rules
For a node type the built-in schema doesn't know, write a rule: a tag, attribute templates, and a content slot. Declarative rules render in the transaction, no callback:
use ;
use ;
let rules = parse.unwrap;
let doc = new;
let txn = doc.transact;
let fragment = txn.get_xml_fragment.expect;
let segments = render_segments.expect;
let html = flatten.into_html.expect;
// A stored <callout kind="warning"> renders as
// <aside class="callout callout--warning">…</aside>
An attribute template mixes literal parts (lit) with stored attributes
(ref), and drops out when it resolves empty. content is inline for
formatted text, blocks for child blocks, or none for a leaf; inline
is the default. void: true drops the closing tag. Point a rule at a
built-in type to override it. This crate has no marks tier. Lexical keeps
formatting in the text model, and that renders on its own. For marks, use
prosemirror-yjs-html.
Callback rules
Some nodes need real work, like a database lookup. Mark the rule
callback and the renderer hands them back to you. Deferred nodes come
back as segments with their type, attributes as JSON, and rendered
children. The render never runs your code. You splice the result in after
it returns:
use ;
use ;
let rules = parse.unwrap;
let doc = new;
let txn = doc.transact;
let fragment = txn.get_xml_fragment.expect;
let segments = render_segments.expect;
let html = splice;
Rules, Segment, and flatten are re-exported here. Depend on this
crate; yjs-html-core is
internal.
Schema discovery
Editors name their types and attributes however they like, and Lexical
prefixes its own props with __. collect_node_types reports what a
document actually holds:
use ;
use ;
let doc = new;
let txn = doc.transact;
let fragment = txn.get_xml_fragment.expect;
for in collect_node_types.unwrap_or_default
Anything where is_builtin is false needs a rule. Without one, the node
still renders its text and child blocks, just unwrapped.
License
MIT. Developed in yrby, where it backs
Y::Lexical and Y::Lexxy.