yrby 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +71 -0
  3. data/Cargo.lock +29 -29
  4. data/README.md +582 -380
  5. data/ext/yrby/Cargo.toml +3 -3
  6. data/ext/yrby/crates/html-core/Cargo.toml +5 -4
  7. data/ext/yrby/crates/html-core/src/lib.rs +16 -20
  8. data/ext/yrby/crates/lexical-html/Cargo.toml +5 -4
  9. data/ext/yrby/crates/lexical-html/src/lib.rs +57 -48
  10. data/ext/yrby/crates/prosemirror-html/Cargo.toml +5 -4
  11. data/ext/yrby/crates/prosemirror-html/src/lib.rs +151 -104
  12. data/ext/yrby/src/lib.rs +30 -15
  13. data/ext/yrby/src/protocol.rs +17 -17
  14. data/ext/yrby/src/read.rs +137 -26
  15. data/lib/generators/yrby/install/install_generator.rb +24 -13
  16. data/lib/generators/yrby/install/templates/document_channel.rb +4 -7
  17. data/lib/generators/yrby/tables/tables_generator.rb +7 -4
  18. data/lib/generators/yrby/tables/templates/create_y_tables.rb +1 -1
  19. data/lib/y/collaborative/attribute.rb +53 -0
  20. data/lib/y/collaborative/helper.rb +48 -0
  21. data/lib/y/collaborative.rb +98 -0
  22. data/lib/y/lexxy.rb +2 -2
  23. data/lib/y/rendering.rb +15 -15
  24. data/lib/y/tiptap.rb +3 -3
  25. data/lib/y/version.rb +1 -1
  26. data/lib/y.rb +1 -1
  27. metadata +4 -9
  28. data/ext/yrby/target/debug/build/clang-sys-e3ae45bd384f74c3/out/common.rs +0 -355
  29. data/ext/yrby/target/debug/build/clang-sys-e3ae45bd384f74c3/out/dynamic.rs +0 -276
  30. data/ext/yrby/target/debug/build/clang-sys-e3ae45bd384f74c3/out/macros.rs +0 -49
  31. data/ext/yrby/target/debug/build/rb-sys-4407948463231c4f/out/bindings-0.9.128-mri-arm64-darwin23-3.4.7.rs +0 -8934
  32. data/ext/yrby/target/debug/build/rb-sys-dbeea42737529c2d/out/bindings-0.9.128-mri-arm64-darwin23-3.4.7.rs +0 -8934
  33. data/ext/yrby/target/debug/build/serde-58ea0ee887cc2602/out/private.rs +0 -6
  34. data/ext/yrby/target/debug/build/serde_core-41f407c21c1f205e/out/private.rs +0 -5
  35. data/ext/yrby/target/debug/build/thiserror-0f1416a82ff26f22/out/private.rs +0 -5
data/ext/yrby/Cargo.toml CHANGED
@@ -14,9 +14,9 @@ magnus = "0.8"
14
14
  rb-sys = "0.9"
15
15
  yrs = { version = "0.27", features = ["sync"] }
16
16
  serde_json = "1.0"
17
- yrs-html-core = { path = "crates/html-core" }
18
- yrs-lexical-html = { path = "crates/lexical-html" }
19
- yrs-prosemirror-html = { path = "crates/prosemirror-html" }
17
+ yjs-html-core = { path = "crates/html-core" }
18
+ lexical-yjs-html = { path = "crates/lexical-html" }
19
+ prosemirror-yjs-html = { path = "crates/prosemirror-html" }
20
20
 
21
21
  [dev-dependencies]
22
22
  rb-sys-env = "0.1"
@@ -1,14 +1,15 @@
1
1
  [package]
2
- name = "yrs-html-core"
3
- description = "Internal core for the yrs-*-html renderer crates; depend on those instead"
2
+ name = "yjs-html-core"
3
+ description = "Internal core for the lexical-yjs-html and prosemirror-yjs-html renderer crates; depend on those instead"
4
4
  repository = "https://github.com/jpcamara/yrby"
5
5
  readme = "README.md"
6
- version = "0.1.0"
6
+ version = "0.1.3"
7
7
  edition = "2024"
8
8
  rust-version = "1.85"
9
9
  authors = ["JP Camara <johnpcamara@gmail.com>"]
10
10
  license = "MIT"
11
- publish = false
11
+ keywords = ["yjs", "crdt", "yrs", "html", "rendering"]
12
+ categories = ["text-processing", "web-programming"]
12
13
 
13
14
  [dependencies]
14
15
  yrs = { version = "0.27", features = ["sync"] }
@@ -1,21 +1,21 @@
1
- //! Custom render rules and segmented output — the extensibility core shared
2
- //! by both HTML renderers.
1
+ //! The custom render rules and segmented output that both HTML renderers
2
+ //! share.
3
3
  //!
4
- //! Callers register per-node rules. Two tiers:
4
+ //! Callers register a rule per node. There are two tiers:
5
5
  //!
6
- //! - **Declarative rules** (tag, attributes, text, content slot) compile to
7
- //! [`NodeRule`]/[`MarkRule`] here and render natively, inside the document
6
+ //! - A declarative rule (tag, attributes, text, content slot) compiles to a
7
+ //! [`NodeRule`] or [`MarkRule`] and renders natively, inside the document
8
8
  //! transaction, at full speed. This covers the tiptap-php `renderHTML`
9
9
  //! shape: markup as data.
10
- //! - **Callback rules** defer to the caller. Rendering never runs app code
11
- //! while the document is locked — the renderer emits [`Segment::Deferred`]
12
- //! entries carrying the node's type, attributes (as JSON), and its
13
- //! already-rendered children, and the caller fills them in after the
14
- //! render returns. (In the Ruby gem, that caller is the app's block, run
15
- //! once the transaction has closed and the GVL is held again.)
10
+ //! - A callback rule defers to the caller. The renderer never runs
11
+ //! application code while the document is locked. It emits
12
+ //! [`Segment::Deferred`] entries with the node type, the attributes as
13
+ //! JSON, and the already-rendered children, and the caller fills them in
14
+ //! after the render returns. In the Ruby gem that caller is the app's
15
+ //! block, run once the transaction has closed and the GVL is held again.
16
16
  //!
17
- //! Rules arrive as one JSON document (see `parse`), so the same format
18
- //! serves any binding or caller.
17
+ //! Rules arrive as one JSON document (see `parse`), so one format serves
18
+ //! every binding and caller.
19
19
 
20
20
  use std::collections::{BTreeMap, BTreeSet, HashMap};
21
21
  use yrs::{Any, Out, ReadTxn, Xml};
@@ -196,11 +196,7 @@ pub fn resolve_parts<F: Fn(&str) -> Option<String>>(
196
196
  }
197
197
  }
198
198
  }
199
- if out.is_empty() {
200
- None
201
- } else {
202
- Some(out)
203
- }
199
+ if out.is_empty() { None } else { Some(out) }
204
200
  }
205
201
 
206
202
  /// An attribute reference on a node: rules say `:kind`; Lexical stores its own
@@ -384,7 +380,7 @@ fn parse_node_rule(name: &str, spec: &serde_json::Value) -> Result<NodeRule, Str
384
380
  Some(other) => {
385
381
  return Err(format!(
386
382
  "rule for {name:?}: unknown content kind {other:?} (blocks|inline|none)"
387
- ))
383
+ ));
388
384
  }
389
385
  };
390
386
  if spec
@@ -431,7 +427,7 @@ fn parse_attrs(
431
427
  Some(_) => {
432
428
  return Err(format!(
433
429
  "rule for {name:?}: attrs must be an array of [name, template] pairs"
434
- ))
430
+ ));
435
431
  }
436
432
  };
437
433
  for entry in entries {
@@ -1,16 +1,17 @@
1
1
  [package]
2
- name = "yrs-lexical-html"
2
+ name = "lexical-yjs-html"
3
3
  description = "Render Lexical-shaped yrs documents to HTML, no browser or Node required"
4
4
  repository = "https://github.com/jpcamara/yrby"
5
5
  readme = "README.md"
6
- version = "0.1.0"
6
+ version = "0.1.3"
7
7
  edition = "2024"
8
8
  rust-version = "1.85"
9
9
  authors = ["JP Camara <johnpcamara@gmail.com>"]
10
10
  license = "MIT"
11
- publish = false
11
+ keywords = ["yjs", "crdt", "lexical", "html", "yrs"]
12
+ categories = ["text-processing", "web-programming"]
12
13
 
13
14
  [dependencies]
14
15
  yrs = { version = "0.27", features = ["sync"] }
15
16
  serde_json = "1.0"
16
- yrs-html-core = { path = "../html-core" }
17
+ yjs-html-core = { path = "../html-core", version = "0.1.3" }
@@ -1,48 +1,61 @@
1
- //! Native HTML rendering of Lexical documents from the yrs collab structure —
2
- //! no Node process, no headless editor.
1
+ //! HTML for Lexical documents, rendered from the Yjs structure the editor
2
+ //! syncs. No Node process and no headless editor. A server that holds the
3
+ //! document bytes produces the markup itself.
3
4
  //!
4
- //! This renders **core Lexical**: paragraphs, headings, quotes, code, lists
5
- //! and list items, tables, horizontal rules, links, and the whole text-format
6
- //! model. Everything Lexxy-specific — its own node types (attachments,
5
+ //! This renders core Lexical: paragraphs, headings, quotes, code blocks,
6
+ //! lists and list items, tables, horizontal rules, links, and the whole
7
+ //! text-format model. Lexxy's additions live in the Ruby layer, as the
8
+ //! `Y::Lexxy` renderer's rule set: its own node types (attachments,
7
9
  //! galleries, `early_escape_code`, `horizontal_divider`) and its decorations
8
- //! of core nodes (the table figure wrapper, header-cell styling, the
9
- //! nested-list-item class) — lives in the Ruby layer as the `Y::Lexxy`
10
- //! renderer's rule set, built on the same extension API apps use
11
- //! (`Y::Lexical` is the core base class). The Lexxy byte-parity guarantee is
12
- //! held there: the Ruby fixture tests and the live headless-Chrome e2e pin
13
- //! `Y::Lexxy#to_html` against a real editor's own serialized value. The
14
- //! native tests pin core output as regression goldens (stock Lexical has no
15
- //! canonical serializer to capture against).
10
+ //! of core nodes (the figure around a table, header-cell styling, the
11
+ //! nested-list-item class). That rule set uses the same extension API
12
+ //! applications use. `Y::Lexical` is the core base class. The byte-parity
13
+ //! guarantee is held there too: the Ruby fixture tests and the
14
+ //! headless-Chrome end-to-end run pin `Y::Lexxy#to_html` against a real
15
+ //! editor's serialized value. The tests in this crate pin core output as
16
+ //! regression goldens. Stock Lexical has no canonical serializer to capture
17
+ //! from.
16
18
  //!
17
- //! Prior art: `ueberdosis/tiptap-php` renders ProseMirror JSON to HTML in pure
18
- //! PHP — a schema-pinned renderer outside the JS runtime. This works from the
19
- //! collab (Yjs) structure rather than JSON.
19
+ //! Prior art: `ueberdosis/tiptap-php` renders ProseMirror JSON to HTML in
20
+ //! plain PHP, outside any JavaScript runtime. This crate does the same job
21
+ //! from the collaborative (Yjs) structure.
20
22
  //!
21
- //! Storage model (verified against bytes captured from a live editor):
22
- //! - Blocks are `Y.XmlText` with a `__type` attribute (`paragraph`, `heading`
23
- //! (+`__tag`), `quote`, `code` (+`__language`), `list`/`listitem`,
24
- //! `table`/`tablerow`/`tablecell`, `link`/`autolink` (inline)).
25
- //! - Text runs are preceded by an embedded `Y.Map` carrying per-run metadata
26
- //! (`__type: "text" | "code-highlight" | "tab"`, `__format` bitmask).
27
- //! - `linebreak` is a bare metadata map; `tab` is a map followed by a "\t" run.
28
- //! - Decorator nodes are `Y.XmlElement`s with their fields as plain
29
- //! attributes (`horizontalrule` here; app/Lexxy decorators via rules).
23
+ //! How Lexical stores a document, checked against bytes captured from a live
24
+ //! editor:
30
25
  //!
31
- //! Text-format rendering follows the export pipeline Lexxy runs (Lexical's
32
- //! `$generateHtmlFromNodes` + sanitize), which is the only externally
33
- //! pinnable truth for formatting: inner tag `strong` (bold) / `em` (italic,
34
- //! when not bold); outer tag `code` / `mark` / `sub` / `sup`; an `<i>` wrap
35
- //! only when bold+italic combine (the `em` slot is taken); `<s>` / `<u>`
36
- //! wraps always; `<span>`s are unwrapped, so unformatted text is bare. A
37
- //! run's `__style` (highlight colors) survives on the createDOM tag,
38
- //! filtered to color/background-color; on a plain or s/u-only run it dies
39
- //! with the unwrapped span. Case-transform format bits are never rendered
40
- //! (their text-transform style is outside the sanitize whitelist).
26
+ //! - Blocks are `Y.XmlText` with a `__type` attribute: `paragraph`,
27
+ //! `heading` (plus `__tag`), `quote`, `code` (plus `__language`), `list`
28
+ //! and `listitem`, `table`, `tablerow` and `tablecell`, and the inline
29
+ //! `link` and `autolink`.
30
+ //! - Each text run is preceded by an embedded `Y.Map` with its metadata: a
31
+ //! `__type` of `text`, `code-highlight`, or `tab`, and a `__format`
32
+ //! bitmask.
33
+ //! - A `linebreak` is a bare metadata map. A `tab` is a map followed by a
34
+ //! `"\t"` run.
35
+ //! - Decorator nodes are `Y.XmlElement`s whose fields are plain attributes.
36
+ //! `horizontalrule` is handled here. Application and Lexxy decorators come
37
+ //! in through rules.
41
38
  //!
42
- //! Custom nodes: rules are registered by `__type` (see `yrs-html-core`) and
43
- //! consulted before the built-in arms, so they extend the schema or override
44
- //! a built-in. Declarative rules render here; callback rules emit
45
- //! `Segment::Deferred` for the caller to fill in after the render.
39
+ //! Text formatting follows the export pipeline Lexxy runs: Lexical's
40
+ //! `$generateHtmlFromNodes`, then sanitize. That output is the only
41
+ //! formatting truth that can be pinned from outside. The inner tag is
42
+ //! `strong` for bold, or `em` for italic without bold. The outer tag is
43
+ //! `code`, `mark`, `sub`, or `sup`. An `<i>` wraps only when bold and italic
44
+ //! combine, because the `em` slot is taken. `<s>` and `<u>` wrap whenever
45
+ //! present. `<span>`s are unwrapped, so unformatted text is bare. A run's
46
+ //! `__style` (highlight colors) survives on the createDOM tag, filtered to
47
+ //! color and background-color. On a plain run, or one with only strike or
48
+ //! underline, it goes away with the span. The case-transform format bits
49
+ //! never render; their text-transform style is outside the sanitize
50
+ //! whitelist.
51
+ //!
52
+ //! A node type with no rule still renders its text and child blocks, just
53
+ //! unwrapped.
54
+ //!
55
+ //! Custom nodes register rules by `__type` (see `yjs-html-core`). A rule is
56
+ //! consulted before the built-in arms, so it can extend the schema or
57
+ //! replace a built-in. Declarative rules render here. Callback rules emit
58
+ //! `Segment::Deferred` for the caller to fill in after the render returns.
46
59
 
47
60
  // README examples are living code: compile-checked on every cargo test.
48
61
  #[cfg(doctest)]
@@ -50,8 +63,8 @@
50
63
  mod readme_examples {}
51
64
 
52
65
  // The full rules surface, re-exported: depend on this crate alone;
53
- // yrs-html-core is an internal implementation crate.
54
- pub use yrs_html_core::*;
66
+ // yjs-html-core is an internal implementation crate.
67
+ pub use yjs_html_core::*;
55
68
  use yrs::types::text::YChange;
56
69
  use yrs::{
57
70
  Any, GetString, Map, Out, ReadTxn, Text, Xml, XmlElementRef, XmlFragment, XmlFragmentRef,
@@ -130,7 +143,7 @@ pub fn render_segments<T: ReadTxn>(
130
143
  /// callback rules, segments always flatten.
131
144
  pub fn render<T: ReadTxn>(txn: &T, fragment: &XmlFragmentRef) -> Option<String> {
132
145
  render_segments(txn, fragment, &Rules::empty()).map(|segs| {
133
- yrs_html_core::flatten(segs)
146
+ yjs_html_core::flatten(segs)
134
147
  .into_html()
135
148
  .expect("no callback rules registered")
136
149
  })
@@ -773,11 +786,7 @@ fn lexxy_style(style: &str) -> Option<String> {
773
786
  css.push(';');
774
787
  }
775
788
  }
776
- if css.is_empty() {
777
- None
778
- } else {
779
- Some(css)
780
- }
789
+ if css.is_empty() { None } else { Some(css) }
781
790
  }
782
791
 
783
792
  /// `link` / `autolink`: Lexxy's sanitize keeps only `href` and `title`
@@ -1301,7 +1310,7 @@ mod tests {
1301
1310
  let txn = doc.transact();
1302
1311
  let frag = txn.get_xml_fragment("root").unwrap();
1303
1312
  let segs = render_segments(&txn, &frag, &rules).unwrap();
1304
- let html = yrs_html_core::flatten(segs).into_html().unwrap();
1313
+ let html = yjs_html_core::flatten(segs).into_html().unwrap();
1305
1314
  assert_eq!(
1306
1315
  html,
1307
1316
  "<p><a class=\"app-link\" href=\"https://x.example\">site</a>\
@@ -1,16 +1,17 @@
1
1
  [package]
2
- name = "yrs-prosemirror-html"
2
+ name = "prosemirror-yjs-html"
3
3
  description = "Render ProseMirror-shaped yrs documents to HTML, no browser or Node required"
4
4
  repository = "https://github.com/jpcamara/yrby"
5
5
  readme = "README.md"
6
- version = "0.1.0"
6
+ version = "0.1.4"
7
7
  edition = "2024"
8
8
  rust-version = "1.85"
9
9
  authors = ["JP Camara <johnpcamara@gmail.com>"]
10
10
  license = "MIT"
11
- publish = false
11
+ keywords = ["yjs", "crdt", "prosemirror", "tiptap", "html"]
12
+ categories = ["text-processing", "web-programming"]
12
13
 
13
14
  [dependencies]
14
15
  yrs = { version = "0.27", features = ["sync"] }
15
16
  serde_json = "1.0"
16
- yrs-html-core = { path = "../html-core" }
17
+ yjs-html-core = { path = "../html-core", version = "0.1.3" }