prosereflect 0.1.1 → 0.3.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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +63 -0
  3. data/.github/workflows/links.yml +97 -0
  4. data/.github/workflows/rake.yml +4 -0
  5. data/.github/workflows/release.yml +5 -0
  6. data/.gitignore +4 -0
  7. data/.rubocop.yml +19 -1
  8. data/.rubocop_todo.yml +119 -183
  9. data/CLAUDE.md +78 -0
  10. data/Gemfile +8 -4
  11. data/README.adoc +2 -0
  12. data/Rakefile +3 -3
  13. data/docs/Gemfile +10 -0
  14. data/docs/INDEX.adoc +45 -0
  15. data/docs/_advanced/index.adoc +15 -0
  16. data/docs/_advanced/schema.adoc +112 -0
  17. data/docs/_advanced/step-map.adoc +66 -0
  18. data/docs/_advanced/steps.adoc +88 -0
  19. data/docs/_advanced/test-builder.adoc +61 -0
  20. data/docs/_advanced/transform.adoc +92 -0
  21. data/docs/_config.yml +174 -0
  22. data/docs/_features/html-input.adoc +69 -0
  23. data/docs/_features/html-output.adoc +45 -0
  24. data/docs/_features/index.adoc +15 -0
  25. data/docs/_features/marks.adoc +86 -0
  26. data/docs/_features/node-types.adoc +124 -0
  27. data/docs/_features/user-mentions.adoc +47 -0
  28. data/docs/_guides/custom-nodes.adoc +107 -0
  29. data/docs/_guides/index.adoc +13 -0
  30. data/docs/_guides/round-trip-html.adoc +91 -0
  31. data/docs/_guides/serialization.adoc +109 -0
  32. data/docs/_pages/index.adoc +67 -0
  33. data/docs/_reference/document-api.adoc +49 -0
  34. data/docs/_reference/index.adoc +14 -0
  35. data/docs/_reference/node-api.adoc +79 -0
  36. data/docs/_reference/schema-api.adoc +95 -0
  37. data/docs/_reference/transform-api.adoc +77 -0
  38. data/docs/_understanding/document-model.adoc +65 -0
  39. data/docs/_understanding/fragment.adoc +52 -0
  40. data/docs/_understanding/index.adoc +14 -0
  41. data/docs/_understanding/resolved-position.adoc +53 -0
  42. data/docs/_understanding/slice.adoc +54 -0
  43. data/docs/lychee.toml +63 -0
  44. data/lib/prosereflect/attribute/base.rb +4 -6
  45. data/lib/prosereflect/attribute/bold.rb +2 -4
  46. data/lib/prosereflect/attribute/href.rb +1 -3
  47. data/lib/prosereflect/attribute/id.rb +7 -7
  48. data/lib/prosereflect/attribute.rb +4 -7
  49. data/lib/prosereflect/blockquote.rb +19 -11
  50. data/lib/prosereflect/bullet_list.rb +36 -29
  51. data/lib/prosereflect/code_block.rb +23 -27
  52. data/lib/prosereflect/code_block_wrapper.rb +12 -13
  53. data/lib/prosereflect/document.rb +14 -22
  54. data/lib/prosereflect/fragment.rb +249 -0
  55. data/lib/prosereflect/hard_break.rb +6 -6
  56. data/lib/prosereflect/heading.rb +14 -15
  57. data/lib/prosereflect/horizontal_rule.rb +23 -14
  58. data/lib/prosereflect/image.rb +32 -23
  59. data/lib/prosereflect/input/html.rb +179 -104
  60. data/lib/prosereflect/input.rb +7 -0
  61. data/lib/prosereflect/list_item.rb +11 -12
  62. data/lib/prosereflect/mark/base.rb +9 -11
  63. data/lib/prosereflect/mark/bold.rb +1 -3
  64. data/lib/prosereflect/mark/code.rb +1 -3
  65. data/lib/prosereflect/mark/italic.rb +1 -3
  66. data/lib/prosereflect/mark/link.rb +1 -3
  67. data/lib/prosereflect/mark/strike.rb +1 -3
  68. data/lib/prosereflect/mark/subscript.rb +1 -3
  69. data/lib/prosereflect/mark/superscript.rb +1 -3
  70. data/lib/prosereflect/mark/underline.rb +1 -3
  71. data/lib/prosereflect/mark.rb +9 -5
  72. data/lib/prosereflect/node.rb +171 -33
  73. data/lib/prosereflect/ordered_list.rb +17 -14
  74. data/lib/prosereflect/output/html.rb +279 -50
  75. data/lib/prosereflect/output.rb +7 -0
  76. data/lib/prosereflect/paragraph.rb +11 -13
  77. data/lib/prosereflect/parser.rb +56 -66
  78. data/lib/prosereflect/resolved_pos.rb +256 -0
  79. data/lib/prosereflect/schema/attribute.rb +57 -0
  80. data/lib/prosereflect/schema/content_match.rb +656 -0
  81. data/lib/prosereflect/schema/fragment.rb +166 -0
  82. data/lib/prosereflect/schema/mark.rb +121 -0
  83. data/lib/prosereflect/schema/mark_type.rb +130 -0
  84. data/lib/prosereflect/schema/node.rb +236 -0
  85. data/lib/prosereflect/schema/node_type.rb +274 -0
  86. data/lib/prosereflect/schema/schema_main.rb +190 -0
  87. data/lib/prosereflect/schema/spec.rb +92 -0
  88. data/lib/prosereflect/schema.rb +39 -0
  89. data/lib/prosereflect/table.rb +12 -13
  90. data/lib/prosereflect/table_cell.rb +13 -13
  91. data/lib/prosereflect/table_header.rb +17 -17
  92. data/lib/prosereflect/table_row.rb +12 -12
  93. data/lib/prosereflect/text.rb +35 -11
  94. data/lib/prosereflect/transform/attr_step.rb +157 -0
  95. data/lib/prosereflect/transform/insert_step.rb +115 -0
  96. data/lib/prosereflect/transform/mapping.rb +82 -0
  97. data/lib/prosereflect/transform/mark_step.rb +269 -0
  98. data/lib/prosereflect/transform/replace_around_step.rb +181 -0
  99. data/lib/prosereflect/transform/replace_step.rb +157 -0
  100. data/lib/prosereflect/transform/slice.rb +91 -0
  101. data/lib/prosereflect/transform/step.rb +89 -0
  102. data/lib/prosereflect/transform/step_map.rb +126 -0
  103. data/lib/prosereflect/transform/structure.rb +120 -0
  104. data/lib/prosereflect/transform/transform.rb +341 -0
  105. data/lib/prosereflect/transform.rb +26 -0
  106. data/lib/prosereflect/user.rb +15 -15
  107. data/lib/prosereflect/version.rb +1 -1
  108. data/lib/prosereflect.rb +30 -17
  109. data/prosereflect.gemspec +17 -16
  110. data/spec/fixtures/documents/formatted_text.yaml +14 -0
  111. data/spec/fixtures/documents/heading_paragraph.yaml +16 -0
  112. data/spec/fixtures/documents/lists_doc.yaml +32 -0
  113. data/spec/fixtures/documents/mixed_content.yaml +40 -0
  114. data/spec/fixtures/documents/nested_doc.yaml +20 -0
  115. data/spec/fixtures/documents/simple_doc.yaml +6 -0
  116. data/spec/fixtures/documents/table_doc.yaml +32 -0
  117. data/spec/fixtures/documents/transform_test.yaml +14 -0
  118. data/spec/fixtures/schema/custom_schema.rb +37 -0
  119. data/spec/fixtures/schema/test_schema.rb +46 -0
  120. data/spec/fixtures/test_builder/helpers.rb +212 -0
  121. data/spec/prosereflect/document_spec.rb +332 -330
  122. data/spec/prosereflect/fragment_spec.rb +273 -0
  123. data/spec/prosereflect/hard_break_spec.rb +125 -125
  124. data/spec/prosereflect/input/html_spec.rb +718 -522
  125. data/spec/prosereflect/node_spec.rb +311 -182
  126. data/spec/prosereflect/output/html_spec.rb +105 -105
  127. data/spec/prosereflect/output/whitespace_spec.rb +248 -0
  128. data/spec/prosereflect/paragraph_spec.rb +275 -274
  129. data/spec/prosereflect/parser/round_trip_spec.rb +472 -0
  130. data/spec/prosereflect/parser_spec.rb +185 -180
  131. data/spec/prosereflect/resolved_pos_spec.rb +74 -0
  132. data/spec/prosereflect/schema/conftest.rb +68 -0
  133. data/spec/prosereflect/schema/content_match_spec.rb +237 -0
  134. data/spec/prosereflect/schema/mark_spec.rb +274 -0
  135. data/spec/prosereflect/schema/mark_type_spec.rb +86 -0
  136. data/spec/prosereflect/schema/node_type_spec.rb +142 -0
  137. data/spec/prosereflect/schema/schema_spec.rb +194 -0
  138. data/spec/prosereflect/table_cell_spec.rb +183 -183
  139. data/spec/prosereflect/table_row_spec.rb +149 -149
  140. data/spec/prosereflect/table_spec.rb +320 -318
  141. data/spec/prosereflect/test_builder/marks_spec.rb +127 -0
  142. data/spec/prosereflect/text_spec.rb +133 -132
  143. data/spec/prosereflect/transform/equivalence_spec.rb +487 -0
  144. data/spec/prosereflect/transform/mapping_spec.rb +226 -0
  145. data/spec/prosereflect/transform/replace_spec.rb +832 -0
  146. data/spec/prosereflect/transform/replace_step_spec.rb +157 -0
  147. data/spec/prosereflect/transform/slice_spec.rb +48 -0
  148. data/spec/prosereflect/transform/step_map_spec.rb +70 -0
  149. data/spec/prosereflect/transform/step_spec.rb +211 -0
  150. data/spec/prosereflect/transform/structure_spec.rb +98 -0
  151. data/spec/prosereflect/transform/transform_spec.rb +238 -0
  152. data/spec/prosereflect/user_spec.rb +31 -28
  153. data/spec/prosereflect_spec.rb +28 -26
  154. data/spec/spec_helper.rb +7 -6
  155. data/spec/support/matchers.rb +6 -6
  156. data/spec/support/shared_examples.rb +49 -49
  157. metadata +96 -5
  158. data/spec/prosereflect/version_spec.rb +0 -11
data/docs/_config.yml ADDED
@@ -0,0 +1,174 @@
1
+ # Jekyll configuration for Prosereflect documentation
2
+ # Uses Just the Docs theme - https://just-the-docs.com/
3
+
4
+ # Site settings
5
+ title: Prosereflect Documentation
6
+ description: Ruby library for parsing, manipulating, and traversing ProseMirror rich text editor document trees
7
+ url: https://metanorma.org
8
+ baseurl: /prosereflect
9
+
10
+ # Repository
11
+ repository: metanorma/prosereflect
12
+
13
+ # Theme
14
+ theme: just-the-docs
15
+ remote_theme: just-the-docs/just-the-docs@v0.7.0
16
+ color_scheme: light
17
+
18
+ # AsciiDoc support
19
+ asciidoc: {}
20
+ asciidoctor:
21
+ attributes:
22
+ idprefix: ''
23
+ idseparator: '-'
24
+ source-highlighter: rouge
25
+ icons: font
26
+ experimental: true
27
+ sectanchors: true
28
+ linkattrs: true
29
+ sectnums: true
30
+ toc: left
31
+ toclevels: 3
32
+
33
+ # Search configuration
34
+ search_enabled: true
35
+ search:
36
+ heading_level: 3
37
+ previews: 3
38
+ preview_words_before: 5
39
+ preview_words_after: 10
40
+ tokenizer_separator: /[\s/]+/
41
+ rel_url: true
42
+ button: true
43
+
44
+ # Navigation
45
+ nav_sort: case_insensitive
46
+ nav_fold: true
47
+
48
+ # External links
49
+ aux_links:
50
+ "Source":
51
+ - "https://github.com/metanorma/prosereflect"
52
+ "Report Issues":
53
+ - "https://github.com/metanorma/prosereflect/issues"
54
+
55
+ aux_links_new_tab: true
56
+
57
+ # Back to top
58
+ back_to_top: true
59
+ back_to_top_text: "Back to top"
60
+
61
+ # Heading anchors
62
+ heading_anchors: true
63
+
64
+ # Footer
65
+ footer_content: 'Copyright &copy; 2025 Ribose. Distributed under the <a href="https://github.com/metanorma/prosereflect/blob/main/LICENSE">BSD 2-Clause License</a>.'
66
+
67
+ # Footer last edit timestamp
68
+ last_edit_timestamp: true
69
+ last_edit_time_format: "%b %e %Y at %I:%M %p"
70
+
71
+ # Enable code copy button
72
+ enable_copy_code_button: true
73
+
74
+ # Callouts
75
+ callouts_level: quiet
76
+ callouts:
77
+ highlight:
78
+ color: yellow
79
+ important:
80
+ title: Important
81
+ color: blue
82
+ new:
83
+ title: New
84
+ color: green
85
+ note:
86
+ title: Note
87
+ color: purple
88
+ warning:
89
+ title: Warning
90
+ color: red
91
+
92
+ # Plugins
93
+ plugins:
94
+ - jekyll-asciidoc
95
+ - jekyll-seo-tag
96
+ - jekyll-sitemap
97
+
98
+ # Markdown settings (for any markdown files)
99
+ markdown: kramdown
100
+ kramdown:
101
+ input: GFM
102
+ hard_wrap: false
103
+ syntax_highlighter: rouge
104
+
105
+ # Collections for organizing content
106
+ collections:
107
+ pages:
108
+ permalink: "/:path/"
109
+ output: true
110
+ features:
111
+ permalink: "/:collection/:path/"
112
+ output: true
113
+ understanding:
114
+ permalink: "/:collection/:path/"
115
+ output: true
116
+ advanced:
117
+ permalink: "/:collection/:path/"
118
+ output: true
119
+ guides:
120
+ permalink: "/:collection/:path/"
121
+ output: true
122
+ reference:
123
+ permalink: "/:collection/:path/"
124
+ output: true
125
+
126
+ # Just the Docs collection configuration
127
+ just_the_docs:
128
+ collections:
129
+ pages:
130
+ name: Pages
131
+ nav_fold: false
132
+ features:
133
+ name: Features
134
+ nav_fold: true
135
+ understanding:
136
+ name: Understanding
137
+ nav_fold: true
138
+ advanced:
139
+ name: Advanced
140
+ nav_fold: true
141
+ guides:
142
+ name: Guides
143
+ nav_fold: true
144
+ reference:
145
+ name: Reference
146
+ nav_fold: true
147
+
148
+ # Defaults
149
+ defaults:
150
+ - scope:
151
+ path: ""
152
+ type: pages
153
+ values:
154
+ layout: default
155
+
156
+ # Include additional files
157
+ include:
158
+ - "*.adoc"
159
+
160
+ # Exclude from processing
161
+ exclude:
162
+ - Gemfile
163
+ - Gemfile.lock
164
+ - node_modules
165
+ - vendor
166
+ - .sass-cache
167
+ - .jekyll-cache
168
+ - .bundle
169
+ - README.md
170
+ - LICENSE
171
+ - .git
172
+ - .gitignore
173
+
174
+ permalink: pretty
@@ -0,0 +1,69 @@
1
+ ---
2
+ layout: default
3
+ title: HTML Input
4
+ parent: Features
5
+ nav_order: 3
6
+ ---
7
+ = HTML Input
8
+
9
+ `Prosereflect::Input::Html` parses HTML strings into ProseMirror document models using Nokogiri.
10
+
11
+ == Basic Parsing
12
+
13
+ [source,ruby]
14
+ ----
15
+ require 'prosereflect'
16
+
17
+ html = '<p>Hello <strong>bold</strong> and <em>italic</em></p>'
18
+ doc = Prosereflect::Input::Html.parse(html)
19
+
20
+ para = doc.paragraphs.first
21
+ para.text_content # => "Hello bold and italic"
22
+ ----
23
+
24
+ == Supported Elements
25
+
26
+ The HTML parser maps standard HTML elements to ProseMirror node types:
27
+
28
+ [cols="2,2"]
29
+ |===
30
+ | HTML | ProseMirror Type
31
+
32
+ | `<p>` | paragraph
33
+ | `<h1>` - `<h6>` | heading (with level attr)
34
+ | `<ul>` / `<ol>` | bullet_list / ordered_list
35
+ | `<li>` | list_item
36
+ | `<table>`, `<tr>`, `<td>`, `<th>` | table, table_row, table_cell, table_header
37
+ | `<blockquote>` | blockquote
38
+ | `<pre><code>` | code_block_wrapper / code_block
39
+ | `<img>` | image
40
+ | `<hr>` | horizontal_rule
41
+ | `<br>` | hard_break
42
+ | `<user-mention>` | user
43
+ |===
44
+
45
+ == Supported Inline Formatting
46
+
47
+ [cols="2,2"]
48
+ |===
49
+ | HTML | Mark Type
50
+
51
+ | `<strong>`, `<b>` | bold
52
+ | `<em>`, `<i>` | italic
53
+ | `<code>` | code
54
+ | `<a>` | link (with href)
55
+ | `<s>`, `<del>` | strike
56
+ | `<sub>` | subscript
57
+ | `<sup>` | superscript
58
+ | `<u>` | underline
59
+ |===
60
+
61
+ == User Mentions
62
+
63
+ [source,ruby]
64
+ ----
65
+ html = '<p>Hello <user-mention data-id="123"></user-mention>!</p>'
66
+ doc = Prosereflect::Input::Html.parse(html)
67
+ users = doc.find_all('user')
68
+ users.first.id # => "123"
69
+ ----
@@ -0,0 +1,45 @@
1
+ ---
2
+ layout: default
3
+ title: HTML Output
4
+ parent: Features
5
+ nav_order: 4
6
+ ---
7
+ = HTML Output
8
+
9
+ `Prosereflect::Output::Html` converts ProseMirror document models to HTML strings using Nokogiri.
10
+
11
+ == Basic Conversion
12
+
13
+ [source,ruby]
14
+ ----
15
+ require 'prosereflect'
16
+
17
+ doc = Prosereflect::Document.create
18
+ para = doc.add_paragraph("Hello")
19
+ para.add_text(" bold", [Prosereflect::Mark::Bold.new])
20
+
21
+ html = Prosereflect::Output::Html.convert(doc)
22
+ # => "<html><body><p>Hello<strong> bold</strong></p></body></html>"
23
+ ----
24
+
25
+ == DOMSerializer
26
+
27
+ `Prosereflect::Output::DOMSerializer` provides fine-grained control over HTML output:
28
+
29
+ * `serialize_node(node)` -- serialize a single node
30
+ * `render_text(text, marks)` -- render text with marks applied
31
+
32
+ == Whitespace Handling
33
+
34
+ The serializer handles whitespace differently depending on the parent node type:
35
+
36
+ * **Code blocks** (`code_block`, `code_block_wrapper`): whitespace is preserved exactly
37
+ * **Regular blocks** (`paragraph`, `heading`): whitespace is collapsed (multiple spaces become one)
38
+ * **`white-space: pre`** nodes: whitespace is preserved
39
+
40
+ Methods involved:
41
+
42
+ * `whitespace_mode(node)` -- returns `:preserve` or `:collapse`
43
+ * `collapse_whitespace(text)` -- collapses runs of whitespace
44
+ * `normalize_whitespace(text)` -- replaces tabs/newlines with spaces
45
+ * `process_text_whitespace(text, node)` -- applies the appropriate mode
@@ -0,0 +1,15 @@
1
+ ---
2
+ layout: default
3
+ title: Features
4
+ nav_order: 3
5
+ has_children: true
6
+ ---
7
+ = Features
8
+
9
+ prosereflect provides a comprehensive set of features for working with ProseMirror documents in Ruby.
10
+
11
+ * x:node-types[Node Types] -- All available node types and their attributes
12
+ * x:marks[Marks] -- Text formatting marks (bold, italic, links, etc.)
13
+ * x:html-input[HTML Input] -- Parse HTML into ProseMirror documents
14
+ * x:html-output[HTML Output] -- Serialize documents to HTML
15
+ * x:user-mentions[User Mentions] -- @mention support in documents
@@ -0,0 +1,86 @@
1
+ ---
2
+ layout: default
3
+ title: Marks
4
+ parent: Features
5
+ nav_order: 2
6
+ ---
7
+ = Marks
8
+
9
+ Marks represent inline formatting applied to text nodes. prosereflect supports all standard ProseMirror marks.
10
+
11
+ == Creating Marks
12
+
13
+ [source,ruby]
14
+ ----
15
+ bold = Prosereflect::Mark::Bold.new
16
+ italic = Prosereflect::Mark::Italic.new
17
+ link = Prosereflect::Mark::Link.new(href: "https://example.com")
18
+ ----
19
+
20
+ == Applying Marks to Text
21
+
22
+ When adding text to a node, pass marks as the second argument:
23
+
24
+ [source,ruby]
25
+ ----
26
+ doc = Prosereflect::Document.create
27
+ para = doc.add_paragraph("Plain text")
28
+ para.add_text(" bold", [Prosereflect::Mark::Bold.new])
29
+ para.add_text(" italic", [Prosereflect::Mark::Italic.new])
30
+ para.add_text(" both", [Prosereflect::Mark::Bold.new, Prosereflect::Mark::Italic.new])
31
+ ----
32
+
33
+ == Available Mark Types
34
+
35
+ [cols="1,1,3"]
36
+ |===
37
+ | Class | PM Type | Attributes
38
+
39
+ | `Mark::Bold` | `bold` | none
40
+ | `Mark::Italic` | `italic` | none
41
+ | `Mark::Code` | `code` | none
42
+ | `Mark::Link` | `link` | `href`
43
+ | `Mark::Strike` | `strike` | none
44
+ | `Mark::Subscript` | `subscript` | none
45
+ | `Mark::Superscript` | `superscript` | none
46
+ | `Mark::Underline` | `underline` | none
47
+ |===
48
+
49
+ == Link Marks
50
+
51
+ Link marks carry an `href` attribute:
52
+
53
+ [source,ruby]
54
+ ----
55
+ link_mark = Prosereflect::Mark::Link.new
56
+ link_mark.href = "https://example.com"
57
+ # or via constructor:
58
+ link_mark = Prosereflect::Mark::Link.new(href: "https://example.com")
59
+ ----
60
+
61
+ == Serialization
62
+
63
+ Marks are serialized in the ProseMirror JSON format:
64
+
65
+ [source,ruby]
66
+ ----
67
+ bold = Prosereflect::Mark::Bold.new
68
+ bold.to_h # => {"type" => "bold"}
69
+
70
+ link = Prosereflect::Mark::Link.new(href: "https://example.com")
71
+ link.to_h # => {"type" => "link", "attrs" => {"href" => "https://example.com"}}
72
+ ----
73
+
74
+ == Schema-based Marks
75
+
76
+ When using a Schema, marks can be created with validation:
77
+
78
+ [source,ruby]
79
+ ----
80
+ schema = Prosereflect::Schema.new(
81
+ nodes_spec: { "doc" => { content: "block+" }, "paragraph" => { content: "inline*", group: "block" }, "text" => { group: "inline" } },
82
+ marks_spec: { "bold" => {}, "italic" => {} }
83
+ )
84
+ bold = schema.mark("bold")
85
+ italic = schema.mark("italic")
86
+ ----
@@ -0,0 +1,124 @@
1
+ ---
2
+ layout: default
3
+ title: Node Types
4
+ parent: Features
5
+ nav_order: 1
6
+ ---
7
+ = Node Types
8
+
9
+ prosereflect provides node classes for all standard ProseMirror node types. Each inherits from `Prosereflect::Node` and defines a `PM_TYPE` constant.
10
+
11
+ == Block Nodes
12
+
13
+ === Document
14
+
15
+ Top-level container. Create with `Document.create`:
16
+
17
+ [source,ruby]
18
+ ----
19
+ doc = Prosereflect::Document.create
20
+ doc.add_paragraph("Hello world")
21
+ ----
22
+
23
+ Convenience methods: `tables`, `paragraphs`, `find_first`, `find_all`, `find_children`, `add_paragraph`, `add_heading`, `add_table`, `add_image`, `add_bullet_list`, `add_ordered_list`, `add_blockquote`, `add_horizontal_rule`, `add_code_block_wrapper`, `add_user`.
24
+
25
+ === Paragraph
26
+
27
+ Represents a paragraph of text.
28
+
29
+ [source,ruby]
30
+ ----
31
+ para = Prosereflect::Paragraph.new
32
+ para.add_text("Hello")
33
+ para.text_content # => "Hello"
34
+ ----
35
+
36
+ === Heading
37
+
38
+ Heading with level attribute (1-6):
39
+
40
+ [source,ruby]
41
+ ----
42
+ heading = Prosereflect::Heading.new
43
+ heading.level = 2
44
+ heading.add_text("Title")
45
+ ----
46
+
47
+ === Table, TableRow, TableCell, TableHeader
48
+
49
+ Table structures with convenience methods:
50
+
51
+ [source,ruby]
52
+ ----
53
+ table = doc.add_table
54
+ row = table.add_row
55
+ cell = row.add_cell
56
+ cell.add_paragraph("Data")
57
+ ----
58
+
59
+ Table methods: `header_row`, `data_rows`, `cell_at(row, col)`, `rows`.
60
+
61
+ === Lists
62
+
63
+ `BulletList` and `OrderedList` contain `ListItem` children:
64
+
65
+ [source,ruby]
66
+ ----
67
+ list = doc.add_bullet_list
68
+ item = list.add_item
69
+ item.add_paragraph("First item")
70
+ ----
71
+
72
+ `OrderedList` supports a `start` attribute for numbering.
73
+
74
+ === Blockquote
75
+
76
+ Contains block-level children:
77
+
78
+ [source,ruby]
79
+ ----
80
+ quote = doc.add_blockquote
81
+ quote.add_paragraph("To be or not to be")
82
+ ----
83
+
84
+ === CodeBlockWrapper and CodeBlock
85
+
86
+ Code blocks with syntax highlighting:
87
+
88
+ [source,ruby]
89
+ ----
90
+ wrapper = doc.add_code_block_wrapper
91
+ block = wrapper.add_code_block("puts 'hello'", language: "ruby")
92
+ ----
93
+
94
+ === Image
95
+
96
+ Inline image node with `src`, `alt`, `title`, `width`, `height` attributes.
97
+
98
+ === HorizontalRule
99
+
100
+ Horizontal rule element with `style`, `width`, `thickness` attributes.
101
+
102
+ === HardBreak
103
+
104
+ Inline break element within paragraphs.
105
+
106
+ == Text Nodes
107
+
108
+ `Prosereflect::Text` represents a text node with `text` attribute and optional marks. `node_size` returns `text.length + 1`.
109
+
110
+ == Common Methods
111
+
112
+ All node types inherit from `Node`:
113
+
114
+ * `node_size` -- size in the document position space
115
+ * `text?` -- whether this is a text node
116
+ * `cut(from, to)` -- return a copy with content restricted to range
117
+ * `copy(content)` -- return a copy with different content
118
+ * `find_first(type)`, `find_all(type)`, `find_children(klass)` -- node traversal
119
+ * `add_child(node)` -- append a child
120
+ * `text_content` -- combined text of all descendant text nodes
121
+ * `to_h` -- serialize to ProseMirror JSON hash
122
+ * `resolve(pos)` -- resolve a position to a `ResolvedPos`
123
+ * `nodes_between(from, to)` -- iterate nodes in a range
124
+ * `descendants` -- iterate all descendant nodes
@@ -0,0 +1,47 @@
1
+ ---
2
+ layout: default
3
+ title: User Mentions
4
+ parent: Features
5
+ nav_order: 5
6
+ ---
7
+ = User Mentions
8
+
9
+ prosereflect supports user @mentions as a special inline node type.
10
+
11
+ == Creating Mentions
12
+
13
+ [source,ruby]
14
+ ----
15
+ doc = Prosereflect::Document.create
16
+ para = doc.add_paragraph("Hello ")
17
+ user = Prosereflect::User.new
18
+ user.id = "123"
19
+ para.add_child(user)
20
+ para.add_text("!")
21
+ ----
22
+
23
+ == HTML Representation
24
+
25
+ User mentions are represented as `<user-mention>` elements in HTML:
26
+
27
+ [source,ruby]
28
+ ----
29
+ html = Prosereflect::Output::Html.convert(doc)
30
+ # includes: <user-mention data-id="123"></user-mention>
31
+ ----
32
+
33
+ == Parsing from HTML
34
+
35
+ [source,ruby]
36
+ ----
37
+ html = '<p>Hello <user-mention data-id="123"></user-mention>!</p>'
38
+ doc = Prosereflect::Input::Html.parse(html)
39
+ users = doc.find_all('user')
40
+ users.first.id # => "123"
41
+ ----
42
+
43
+ == Use Cases
44
+
45
+ * Mentioning users in comments or messages
46
+ * Tagging users in collaborative documents
47
+ * Tracking user references in content
@@ -0,0 +1,107 @@
1
+ ---
2
+ layout: default
3
+ title: Custom Nodes
4
+ parent: Guides
5
+ nav_order: 2
6
+ ---
7
+ = Custom Nodes
8
+
9
+ This guide explains how to define custom node types that integrate with prosereflect's serialization and HTML conversion systems.
10
+
11
+ == Defining a Custom Node
12
+
13
+ Create a new class that inherits from `Prosereflect::Node`:
14
+
15
+ [source,ruby]
16
+ ----
17
+ class Callout < Prosereflect::Node
18
+ PM_TYPE = "callout"
19
+
20
+ # Define custom attributes
21
+ attribute :variant, :string
22
+
23
+ # Define serialization mapping
24
+ key_value do
25
+ map "type", to: :type, render_default: true
26
+ map "attrs", to: :attrs
27
+ map "content", to: :content
28
+ end
29
+ end
30
+ ----
31
+
32
+ == Key Concepts
33
+
34
+ === PM_TYPE
35
+
36
+ The `PM_TYPE` constant must match the ProseMirror type string used in JSON serialization. This is how the parser maps JSON data to your Ruby class.
37
+
38
+ === Attributes
39
+
40
+ Use `lutaml-model`'s `attribute` DSL to define custom attributes:
41
+
42
+ [source,ruby]
43
+ ----
44
+ attribute :variant, :string # string attribute
45
+ attribute :count, :integer # integer attribute
46
+ ----
47
+
48
+ Attributes are automatically included in `to_h` output when present in the `attrs` hash.
49
+
50
+ === key_value Block
51
+
52
+ The `key_value` block defines how Ruby attributes map to JSON keys:
53
+
54
+ [source,ruby]
55
+ ----
56
+ key_value do
57
+ map "type", to: :type, render_default: true
58
+ map "attrs", to: :attrs
59
+ map "content", to: :content
60
+ end
61
+ ----
62
+
63
+ === Convenience Methods
64
+
65
+ Add helper methods for construction:
66
+
67
+ [source,ruby]
68
+ ----
69
+ class Callout < Prosereflect::Node
70
+ PM_TYPE = "callout"
71
+
72
+ def self.create(variant: "info", text: nil)
73
+ node = new(type: PM_TYPE, attrs: { "variant" => variant })
74
+ node.add_text(text) if text
75
+ node
76
+ end
77
+ end
78
+ ----
79
+
80
+ == Serialization Round-Trip
81
+
82
+ Ensure `to_h` and `Parser` are compatible:
83
+
84
+ [source,ruby]
85
+ ----
86
+ # Serialize
87
+ callout = Callout.create(variant: "warning", text: "Be careful!")
88
+ hash = callout.to_h
89
+ # => {"type" => "callout", "attrs" => {"variant" => "warning"},
90
+ # "content" => [{"type" => "text", "text" => "Be careful!"}]}
91
+
92
+ # Deserialize (if registered with Parser)
93
+ doc = Prosereflect::Parser.parse_document({
94
+ "type" => "doc",
95
+ "content" => [hash]
96
+ })
97
+ ----
98
+
99
+ == Node Naming Pattern
100
+
101
+ prosereflect maps PM_TYPE strings to classes by convention:
102
+
103
+ * `"paragraph"` -> `Prosereflect::Paragraph`
104
+ * `"heading"` -> `Prosereflect::Heading`
105
+ * `"table"` -> `Prosereflect::Table`
106
+
107
+ Custom types follow the same pattern -- the parser uses `PM_TYPE` to find the matching class.
@@ -0,0 +1,13 @@
1
+ ---
2
+ layout: default
3
+ title: Guides
4
+ nav_order: 6
5
+ has_children: true
6
+ ---
7
+ = Guides
8
+
9
+ Task-oriented tutorials for common prosereflect workflows.
10
+
11
+ * x:round-trip-html[HTML Round-Trip] -- Convert between HTML and ProseMirror
12
+ * x:custom-nodes[Custom Nodes] -- Define custom node types
13
+ * x:serialization[Serialization] -- JSON/YAML round-trips