lexical-yjs-html
Renders the Yjs representation of a Lexical document to HTML, using yrs. Covers core Lexical: paragraphs, headings, quotes, code blocks, lists, tables, links, and the full text-format model. The tests pin the output byte-for-byte against fixtures captured from a live editor.
Usage
[]
= "0.1"
= { = "0.27", = ["sync"] }
The input is a Yjs update: bytes from a durable 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).
An editor may hold several fragments under one doc; pass whichever root
name your editor binds. render returns None when the fragment's shape
is not Lexical's.
Declarative rules
A rule describes how a node type the built-in schema does not know should render: a tag, attribute templates, and a content slot. Declarative rules render inside the document transaction with 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>
Attribute templates concatenate literal parts (lit) and stored-attribute
references (ref); an attribute that resolves empty is omitted. content
is "inline" (formatted text, the default), "blocks" (child block
nodes), or "none" (a leaf). "void": true skips the closing tag. A rule
for a built-in type replaces how that type renders. There is no marks tier
in this crate: Lexical folds formatting into its text model, which renders
natively. prosemirror-yjs-html
has the marks side.
Callback rules
A rule marked callback defers rendering to your code, for nodes that
need logic or a database lookup. Deferred nodes come back as segments
carrying their type, stored attributes as JSON, and already-rendered
children. The render itself never runs your code; you splice the result
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;
The rules surface (Rules, Segment, flatten) is re-exported here;
yjs-html-core is an internal
implementation crate.
Schema discovery
Editors store types and attributes under their own names, and Lexical
prefixes its own props with __. collect_node_types reports what a real
document 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
Types where is_builtin is false need a rule. Without one, an unknown
node still renders its text and nested blocks as plain markup.
License
MIT. Developed in yrby, where it backs
Y::Lexical and Y::Lexxy.