prosereflect 0.1.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.github/workflows/rake.yml +4 -0
- data/.github/workflows/release.yml +5 -0
- data/.rubocop.yml +19 -1
- data/.rubocop_todo.yml +141 -191
- data/CLAUDE.md +78 -0
- data/Gemfile +8 -4
- data/Rakefile +3 -3
- data/lib/prosereflect/attribute/base.rb +4 -6
- data/lib/prosereflect/attribute/bold.rb +2 -4
- data/lib/prosereflect/attribute/href.rb +1 -3
- data/lib/prosereflect/attribute/id.rb +7 -7
- data/lib/prosereflect/attribute.rb +4 -7
- data/lib/prosereflect/blockquote.rb +10 -11
- data/lib/prosereflect/bullet_list.rb +16 -15
- data/lib/prosereflect/code_block.rb +26 -26
- data/lib/prosereflect/code_block_wrapper.rb +12 -13
- data/lib/prosereflect/document.rb +14 -22
- data/lib/prosereflect/hard_break.rb +6 -6
- data/lib/prosereflect/heading.rb +14 -15
- data/lib/prosereflect/horizontal_rule.rb +14 -14
- data/lib/prosereflect/image.rb +23 -23
- data/lib/prosereflect/input/html.rb +83 -104
- data/lib/prosereflect/input.rb +7 -0
- data/lib/prosereflect/list_item.rb +11 -12
- data/lib/prosereflect/mark/base.rb +9 -11
- data/lib/prosereflect/mark/bold.rb +1 -3
- data/lib/prosereflect/mark/code.rb +1 -3
- data/lib/prosereflect/mark/italic.rb +1 -3
- data/lib/prosereflect/mark/link.rb +1 -3
- data/lib/prosereflect/mark/strike.rb +1 -3
- data/lib/prosereflect/mark/subscript.rb +1 -3
- data/lib/prosereflect/mark/superscript.rb +1 -3
- data/lib/prosereflect/mark/underline.rb +1 -3
- data/lib/prosereflect/mark.rb +9 -5
- data/lib/prosereflect/node.rb +31 -31
- data/lib/prosereflect/ordered_list.rb +15 -14
- data/lib/prosereflect/output/html.rb +52 -50
- data/lib/prosereflect/output.rb +7 -0
- data/lib/prosereflect/paragraph.rb +11 -13
- data/lib/prosereflect/parser.rb +47 -66
- data/lib/prosereflect/table.rb +12 -13
- data/lib/prosereflect/table_cell.rb +13 -13
- data/lib/prosereflect/table_header.rb +17 -17
- data/lib/prosereflect/table_row.rb +12 -12
- data/lib/prosereflect/text.rb +11 -11
- data/lib/prosereflect/user.rb +15 -15
- data/lib/prosereflect/version.rb +1 -1
- data/lib/prosereflect.rb +27 -17
- data/prosereflect.gemspec +17 -16
- data/spec/prosereflect/document_spec.rb +332 -330
- data/spec/prosereflect/hard_break_spec.rb +125 -125
- data/spec/prosereflect/input/html_spec.rb +522 -522
- data/spec/prosereflect/node_spec.rb +183 -182
- data/spec/prosereflect/output/html_spec.rb +105 -105
- data/spec/prosereflect/paragraph_spec.rb +275 -274
- data/spec/prosereflect/parser_spec.rb +185 -180
- data/spec/prosereflect/table_cell_spec.rb +183 -183
- data/spec/prosereflect/table_row_spec.rb +149 -149
- data/spec/prosereflect/table_spec.rb +320 -318
- data/spec/prosereflect/text_spec.rb +133 -132
- data/spec/prosereflect/user_spec.rb +31 -28
- data/spec/prosereflect_spec.rb +28 -26
- data/spec/spec_helper.rb +6 -6
- data/spec/support/matchers.rb +6 -6
- data/spec/support/shared_examples.rb +49 -49
- metadata +8 -5
- data/spec/prosereflect/version_spec.rb +0 -11
data/lib/prosereflect/image.rb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'node'
|
|
4
|
-
|
|
5
3
|
module Prosereflect
|
|
6
4
|
# Image class represents a ProseMirror image node.
|
|
7
5
|
# It handles image attributes like src, alt, title, dimensions, etc.
|
|
8
6
|
class Image < Node
|
|
9
|
-
PM_TYPE =
|
|
7
|
+
PM_TYPE = "image"
|
|
10
8
|
|
|
11
|
-
attribute :type, :string, default: -> {
|
|
9
|
+
attribute :type, :string, default: -> {
|
|
10
|
+
self.class.send(:const_get, "PM_TYPE")
|
|
11
|
+
}
|
|
12
12
|
attribute :src, :string
|
|
13
13
|
attribute :alt, :string
|
|
14
14
|
attribute :title, :string
|
|
@@ -17,8 +17,8 @@ module Prosereflect
|
|
|
17
17
|
attribute :attrs, :hash
|
|
18
18
|
|
|
19
19
|
key_value do
|
|
20
|
-
map
|
|
21
|
-
map
|
|
20
|
+
map "type", to: :type, render_default: true
|
|
21
|
+
map "attrs", to: :attrs
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def initialize(attributes = {})
|
|
@@ -27,11 +27,11 @@ module Prosereflect
|
|
|
27
27
|
|
|
28
28
|
# Extract attributes from the attrs hash if provided
|
|
29
29
|
if attributes[:attrs]
|
|
30
|
-
@src = attributes[:attrs][
|
|
31
|
-
@alt = attributes[:attrs][
|
|
32
|
-
@title = attributes[:attrs][
|
|
33
|
-
@width = attributes[:attrs][
|
|
34
|
-
@height = attributes[:attrs][
|
|
30
|
+
@src = attributes[:attrs]["src"]
|
|
31
|
+
@alt = attributes[:attrs]["alt"]
|
|
32
|
+
@title = attributes[:attrs]["title"]
|
|
33
|
+
@width = attributes[:attrs]["width"]
|
|
34
|
+
@height = attributes[:attrs]["height"]
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
super
|
|
@@ -45,35 +45,35 @@ module Prosereflect
|
|
|
45
45
|
def src=(src_url)
|
|
46
46
|
@src = src_url
|
|
47
47
|
self.attrs ||= {}
|
|
48
|
-
attrs[
|
|
48
|
+
attrs["src"] = src_url
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# Update the alt text
|
|
52
52
|
def alt=(alt_text)
|
|
53
53
|
@alt = alt_text
|
|
54
54
|
self.attrs ||= {}
|
|
55
|
-
attrs[
|
|
55
|
+
attrs["alt"] = alt_text
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
# Update the title (tooltip)
|
|
59
59
|
def title=(title_text)
|
|
60
60
|
@title = title_text
|
|
61
61
|
self.attrs ||= {}
|
|
62
|
-
attrs[
|
|
62
|
+
attrs["title"] = title_text
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# Update the width
|
|
66
66
|
def width=(value)
|
|
67
67
|
@width = value
|
|
68
68
|
self.attrs ||= {}
|
|
69
|
-
attrs[
|
|
69
|
+
attrs["width"] = value
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
# Update the height
|
|
73
73
|
def height=(value)
|
|
74
74
|
@height = value
|
|
75
75
|
self.attrs ||= {}
|
|
76
|
-
attrs[
|
|
76
|
+
attrs["height"] = value
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
# Update dimensions (width and height)
|
|
@@ -90,13 +90,13 @@ module Prosereflect
|
|
|
90
90
|
alt: alt,
|
|
91
91
|
title: title,
|
|
92
92
|
width: width,
|
|
93
|
-
height: height
|
|
93
|
+
height: height,
|
|
94
94
|
}.compact
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
# Override content-related methods since images don't have content
|
|
98
98
|
def add_child(*)
|
|
99
|
-
raise NotImplementedError,
|
|
99
|
+
raise NotImplementedError, "Image nodes cannot have children"
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
def content
|
|
@@ -104,23 +104,23 @@ module Prosereflect
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
def src
|
|
107
|
-
@src || attrs&.[](
|
|
107
|
+
@src || attrs&.[]("src")
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
def alt
|
|
111
|
-
@alt || attrs&.[](
|
|
111
|
+
@alt || attrs&.[]("alt")
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
def title
|
|
115
|
-
@title || attrs&.[](
|
|
115
|
+
@title || attrs&.[]("title")
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
def width
|
|
119
|
-
@width || attrs&.[](
|
|
119
|
+
@width || attrs&.[]("width")
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
def height
|
|
123
|
-
@height || attrs&.[](
|
|
123
|
+
@height || attrs&.[]("height")
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
126
|
end
|
|
@@ -1,33 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require_relative '../document'
|
|
5
|
-
require_relative '../paragraph'
|
|
6
|
-
require_relative '../text'
|
|
7
|
-
require_relative '../table'
|
|
8
|
-
require_relative '../table_row'
|
|
9
|
-
require_relative '../table_cell'
|
|
10
|
-
require_relative '../table_header'
|
|
11
|
-
require_relative '../hard_break'
|
|
12
|
-
require_relative '../mark/bold'
|
|
13
|
-
require_relative '../mark/italic'
|
|
14
|
-
require_relative '../mark/code'
|
|
15
|
-
require_relative '../mark/link'
|
|
16
|
-
require_relative '../mark/strike'
|
|
17
|
-
require_relative '../mark/subscript'
|
|
18
|
-
require_relative '../mark/superscript'
|
|
19
|
-
require_relative '../mark/underline'
|
|
20
|
-
require_relative '../attribute/href'
|
|
21
|
-
require_relative '../ordered_list'
|
|
22
|
-
require_relative '../bullet_list'
|
|
23
|
-
require_relative '../list_item'
|
|
24
|
-
require_relative '../blockquote'
|
|
25
|
-
require_relative '../horizontal_rule'
|
|
26
|
-
require_relative '../image'
|
|
27
|
-
require_relative '../code_block_wrapper'
|
|
28
|
-
require_relative '../code_block'
|
|
29
|
-
require_relative '../heading'
|
|
30
|
-
require_relative '../user'
|
|
3
|
+
require "nokogiri"
|
|
31
4
|
|
|
32
5
|
module Prosereflect
|
|
33
6
|
module Input
|
|
@@ -38,7 +11,7 @@ module Prosereflect
|
|
|
38
11
|
html_doc = Nokogiri::HTML(html)
|
|
39
12
|
document = Document.create # Use create instead of new to initialize content array
|
|
40
13
|
|
|
41
|
-
content_node = html_doc.at_css(
|
|
14
|
+
content_node = html_doc.at_css("body") || html_doc.root
|
|
42
15
|
|
|
43
16
|
# Process all child nodes
|
|
44
17
|
process_node_children(content_node, document)
|
|
@@ -65,43 +38,43 @@ module Prosereflect
|
|
|
65
38
|
|
|
66
39
|
# Convert an HTML node to a ProseMirror node
|
|
67
40
|
def convert_node(html_node)
|
|
68
|
-
return nil if html_node.comment? || html_node.text? && html_node.text.strip.empty?
|
|
41
|
+
return nil if html_node.comment? || (html_node.text? && html_node.text.strip.empty?)
|
|
69
42
|
|
|
70
43
|
case html_node.name
|
|
71
|
-
when
|
|
44
|
+
when "text", "#text"
|
|
72
45
|
create_text_node(html_node)
|
|
73
|
-
when
|
|
46
|
+
when "p"
|
|
74
47
|
create_paragraph_node(html_node)
|
|
75
48
|
when /^h([1-6])$/
|
|
76
49
|
create_heading_node(html_node, Regexp.last_match(1).to_i)
|
|
77
|
-
when
|
|
50
|
+
when "br"
|
|
78
51
|
HardBreak.new
|
|
79
|
-
when
|
|
52
|
+
when "table"
|
|
80
53
|
create_table_node(html_node)
|
|
81
|
-
when
|
|
54
|
+
when "tr"
|
|
82
55
|
create_table_row_node(html_node)
|
|
83
|
-
when
|
|
56
|
+
when "th", "td"
|
|
84
57
|
create_table_cell_node(html_node)
|
|
85
|
-
when
|
|
58
|
+
when "ol"
|
|
86
59
|
create_ordered_list_node(html_node)
|
|
87
|
-
when
|
|
60
|
+
when "ul"
|
|
88
61
|
create_bullet_list_node(html_node)
|
|
89
|
-
when
|
|
62
|
+
when "li"
|
|
90
63
|
create_list_item_node(html_node)
|
|
91
|
-
when
|
|
64
|
+
when "blockquote"
|
|
92
65
|
create_blockquote_node(html_node)
|
|
93
|
-
when
|
|
66
|
+
when "hr"
|
|
94
67
|
create_horizontal_rule_node(html_node)
|
|
95
|
-
when
|
|
68
|
+
when "img"
|
|
96
69
|
create_image_node(html_node)
|
|
97
|
-
when
|
|
70
|
+
when "user-mention"
|
|
98
71
|
create_user_node(html_node)
|
|
99
|
-
when
|
|
72
|
+
when "div", "span"
|
|
100
73
|
# For containers, we process their children
|
|
101
74
|
handle_container_node(html_node)
|
|
102
|
-
when
|
|
75
|
+
when "pre"
|
|
103
76
|
create_code_block_wrapper(html_node)
|
|
104
|
-
when
|
|
77
|
+
when "strong", "b", "em", "i", "code", "a", "strike", "s", "del", "sub", "sup", "u"
|
|
105
78
|
# For inline elements with text styling, we handle differently
|
|
106
79
|
handle_styled_text(html_node)
|
|
107
80
|
else
|
|
@@ -126,13 +99,13 @@ module Prosereflect
|
|
|
126
99
|
def create_table_node(html_node)
|
|
127
100
|
table = Table.new
|
|
128
101
|
|
|
129
|
-
thead = html_node.at_css(
|
|
130
|
-
thead&.css(
|
|
102
|
+
thead = html_node.at_css("thead")
|
|
103
|
+
thead&.css("tr")&.each do |tr|
|
|
131
104
|
process_table_row(tr, table, true)
|
|
132
105
|
end
|
|
133
106
|
|
|
134
|
-
tbody = html_node.at_css(
|
|
135
|
-
tbody.css(
|
|
107
|
+
tbody = html_node.at_css("tbody") || html_node
|
|
108
|
+
tbody.css("tr").each do |tr|
|
|
136
109
|
process_table_row(tr, table, false)
|
|
137
110
|
end
|
|
138
111
|
|
|
@@ -142,7 +115,7 @@ module Prosereflect
|
|
|
142
115
|
# Process a table row
|
|
143
116
|
def create_table_row_node(html_node)
|
|
144
117
|
row = TableRow.new
|
|
145
|
-
html_node.css(
|
|
118
|
+
html_node.css("th, td").each do |cell|
|
|
146
119
|
row.add_child(create_table_cell_node(cell))
|
|
147
120
|
end
|
|
148
121
|
row
|
|
@@ -157,13 +130,13 @@ module Prosereflect
|
|
|
157
130
|
# Create a table cell node from HTML cell
|
|
158
131
|
def create_table_cell_node(html_node)
|
|
159
132
|
# Create either a TableHeader or TableCell based on the tag name
|
|
160
|
-
cell = if html_node.name ==
|
|
133
|
+
cell = if html_node.name == "th"
|
|
161
134
|
header = TableHeader.create
|
|
162
135
|
|
|
163
136
|
# Handle header-specific attributes
|
|
164
|
-
header.scope = html_node[
|
|
165
|
-
header.abbr = html_node[
|
|
166
|
-
header.colspan = html_node[
|
|
137
|
+
header.scope = html_node["scope"] if html_node["scope"]
|
|
138
|
+
header.abbr = html_node["abbr"] if html_node["abbr"]
|
|
139
|
+
header.colspan = html_node["colspan"] if html_node["colspan"]
|
|
167
140
|
|
|
168
141
|
header
|
|
169
142
|
else
|
|
@@ -184,7 +157,7 @@ module Prosereflect
|
|
|
184
157
|
# Handle a container-like node (div, span, etc.)
|
|
185
158
|
def handle_container_node(html_node)
|
|
186
159
|
# For top-level divs, process children directly
|
|
187
|
-
if html_node.name ==
|
|
160
|
+
if html_node.name == "div"
|
|
188
161
|
results = []
|
|
189
162
|
html_node.children.each do |child|
|
|
190
163
|
next if child.text? && child.text.strip.empty?
|
|
@@ -224,38 +197,38 @@ module Prosereflect
|
|
|
224
197
|
def handle_styled_text(html_node)
|
|
225
198
|
# Create mark based on the current node
|
|
226
199
|
mark = case html_node.name
|
|
227
|
-
when
|
|
200
|
+
when "strong", "b"
|
|
228
201
|
mark = Mark::Bold.new
|
|
229
|
-
mark.type =
|
|
202
|
+
mark.type = "bold"
|
|
230
203
|
mark
|
|
231
|
-
when
|
|
204
|
+
when "em", "i"
|
|
232
205
|
mark = Mark::Italic.new
|
|
233
|
-
mark.type =
|
|
206
|
+
mark.type = "italic"
|
|
234
207
|
mark
|
|
235
|
-
when
|
|
208
|
+
when "code"
|
|
236
209
|
mark = Mark::Code.new
|
|
237
|
-
mark.type =
|
|
210
|
+
mark.type = "code"
|
|
238
211
|
mark
|
|
239
|
-
when
|
|
212
|
+
when "a"
|
|
240
213
|
mark = Mark::Link.new
|
|
241
|
-
mark.type =
|
|
242
|
-
mark.attrs = {
|
|
214
|
+
mark.type = "link"
|
|
215
|
+
mark.attrs = { "href" => html_node["href"] } if html_node["href"]
|
|
243
216
|
mark
|
|
244
|
-
when
|
|
217
|
+
when "strike", "s", "del"
|
|
245
218
|
mark = Mark::Strike.new
|
|
246
|
-
mark.type =
|
|
219
|
+
mark.type = "strike"
|
|
247
220
|
mark
|
|
248
|
-
when
|
|
221
|
+
when "sub"
|
|
249
222
|
mark = Mark::Subscript.new
|
|
250
|
-
mark.type =
|
|
223
|
+
mark.type = "subscript"
|
|
251
224
|
mark
|
|
252
|
-
when
|
|
225
|
+
when "sup"
|
|
253
226
|
mark = Mark::Superscript.new
|
|
254
|
-
mark.type =
|
|
227
|
+
mark.type = "superscript"
|
|
255
228
|
mark
|
|
256
|
-
when
|
|
229
|
+
when "u"
|
|
257
230
|
mark = Mark::Underline.new
|
|
258
|
-
mark.type =
|
|
231
|
+
mark.type = "underline"
|
|
259
232
|
mark
|
|
260
233
|
end
|
|
261
234
|
|
|
@@ -292,7 +265,8 @@ module Prosereflect
|
|
|
292
265
|
def contains_only_text_or_inline(node)
|
|
293
266
|
node.children.all? do |child|
|
|
294
267
|
child.text? ||
|
|
295
|
-
%w[strong b em i code a br span strike s del sub sup
|
|
268
|
+
%w[strong b em i code a br span strike s del sub sup
|
|
269
|
+
u].include?(child.name) ||
|
|
296
270
|
(child.element? && contains_only_text_or_inline(child))
|
|
297
271
|
end
|
|
298
272
|
end
|
|
@@ -302,11 +276,11 @@ module Prosereflect
|
|
|
302
276
|
list = OrderedList.new
|
|
303
277
|
|
|
304
278
|
# Handle start attribute
|
|
305
|
-
start_val = (html_node[
|
|
279
|
+
start_val = (html_node["start"] || "1").to_i
|
|
306
280
|
list.start = start_val
|
|
307
281
|
|
|
308
282
|
# Process list items
|
|
309
|
-
html_node.css(
|
|
283
|
+
html_node.css("> li").each do |li|
|
|
310
284
|
list.add_child(create_list_item_node(li))
|
|
311
285
|
end
|
|
312
286
|
|
|
@@ -319,11 +293,11 @@ module Prosereflect
|
|
|
319
293
|
list.bullet_style = nil
|
|
320
294
|
|
|
321
295
|
# Handle style attribute if present
|
|
322
|
-
if html_node[
|
|
323
|
-
style = case html_node[
|
|
324
|
-
when /disc/ then
|
|
325
|
-
when /circle/ then
|
|
326
|
-
when /square/ then
|
|
296
|
+
if html_node["style"]&.include?("list-style-type")
|
|
297
|
+
style = case html_node["style"]
|
|
298
|
+
when /disc/ then "disc"
|
|
299
|
+
when /circle/ then "circle"
|
|
300
|
+
when /square/ then "square"
|
|
327
301
|
end
|
|
328
302
|
list.bullet_style = style
|
|
329
303
|
end
|
|
@@ -337,7 +311,9 @@ module Prosereflect
|
|
|
337
311
|
item = ListItem.new
|
|
338
312
|
|
|
339
313
|
# Handle text content first
|
|
340
|
-
text_content = html_node.children.select
|
|
314
|
+
text_content = html_node.children.select do |child|
|
|
315
|
+
child.text? || inline_element?(child)
|
|
316
|
+
end
|
|
341
317
|
if text_content.any?
|
|
342
318
|
paragraph = Paragraph.new
|
|
343
319
|
text_content.each do |child|
|
|
@@ -348,7 +324,9 @@ module Prosereflect
|
|
|
348
324
|
end
|
|
349
325
|
|
|
350
326
|
# Handle nested content
|
|
351
|
-
html_node.children.reject
|
|
327
|
+
html_node.children.reject do |child|
|
|
328
|
+
child.text? || inline_element?(child)
|
|
329
|
+
end.each do |child|
|
|
352
330
|
node = convert_node(child)
|
|
353
331
|
if node.is_a?(Array)
|
|
354
332
|
node.each { |n| item.add_content(n) }
|
|
@@ -364,7 +342,8 @@ module Prosereflect
|
|
|
364
342
|
def inline_element?(node)
|
|
365
343
|
return false unless node.element?
|
|
366
344
|
|
|
367
|
-
%w[strong b em i code a br span strike s del sub sup
|
|
345
|
+
%w[strong b em i code a br span strike s del sub sup
|
|
346
|
+
u].include?(node.name)
|
|
368
347
|
end
|
|
369
348
|
|
|
370
349
|
# Create a blockquote node from HTML blockquote
|
|
@@ -372,7 +351,7 @@ module Prosereflect
|
|
|
372
351
|
quote = Blockquote.new
|
|
373
352
|
|
|
374
353
|
# Handle cite attribute if present
|
|
375
|
-
quote.citation = html_node[
|
|
354
|
+
quote.citation = html_node["cite"] if html_node["cite"]
|
|
376
355
|
|
|
377
356
|
# Process each child separately to maintain block structure
|
|
378
357
|
html_node.children.each do |child|
|
|
@@ -401,8 +380,8 @@ module Prosereflect
|
|
|
401
380
|
hr = HorizontalRule.new
|
|
402
381
|
|
|
403
382
|
# Handle style attributes if present
|
|
404
|
-
if html_node[
|
|
405
|
-
style = html_node[
|
|
383
|
+
if html_node["style"]
|
|
384
|
+
style = html_node["style"]
|
|
406
385
|
|
|
407
386
|
# Parse border-style
|
|
408
387
|
hr.style = Regexp.last_match(1) if style =~ /border-style:\s*(solid|dashed|dotted)/
|
|
@@ -420,20 +399,20 @@ module Prosereflect
|
|
|
420
399
|
# Create an image node from HTML img
|
|
421
400
|
def create_image_node(html_node)
|
|
422
401
|
# Skip images without src
|
|
423
|
-
return nil unless html_node[
|
|
402
|
+
return nil unless html_node["src"]
|
|
424
403
|
|
|
425
404
|
image = Image.new
|
|
426
405
|
|
|
427
406
|
# Handle required src attribute
|
|
428
|
-
image.src = html_node[
|
|
407
|
+
image.src = html_node["src"]
|
|
429
408
|
|
|
430
409
|
# Handle optional attributes
|
|
431
|
-
image.alt = html_node[
|
|
432
|
-
image.title = html_node[
|
|
410
|
+
image.alt = html_node["alt"] if html_node["alt"]
|
|
411
|
+
image.title = html_node["title"] if html_node["title"]
|
|
433
412
|
|
|
434
413
|
# Handle dimensions
|
|
435
|
-
width = html_node[
|
|
436
|
-
height = html_node[
|
|
414
|
+
width = html_node["width"]&.to_i
|
|
415
|
+
height = html_node["height"]&.to_i
|
|
437
416
|
image.dimensions = [width, height] if width || height
|
|
438
417
|
|
|
439
418
|
image
|
|
@@ -443,17 +422,17 @@ module Prosereflect
|
|
|
443
422
|
def create_code_block_wrapper(html_node)
|
|
444
423
|
wrapper = CodeBlockWrapper.new
|
|
445
424
|
wrapper.attrs = {
|
|
446
|
-
|
|
425
|
+
"line_numbers" => false,
|
|
447
426
|
}
|
|
448
427
|
|
|
449
|
-
code_node = html_node.at_css(
|
|
428
|
+
code_node = html_node.at_css("code")
|
|
450
429
|
if code_node
|
|
451
430
|
block = create_code_block(code_node)
|
|
452
431
|
wrapper.add_child(block)
|
|
453
432
|
end
|
|
454
433
|
|
|
455
|
-
wrapper.to_h[
|
|
456
|
-
|
|
434
|
+
wrapper.to_h["attrs"] = {
|
|
435
|
+
"line_numbers" => false,
|
|
457
436
|
}
|
|
458
437
|
wrapper
|
|
459
438
|
end
|
|
@@ -465,9 +444,9 @@ module Prosereflect
|
|
|
465
444
|
language = extract_language(html_node)
|
|
466
445
|
|
|
467
446
|
block.attrs = {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
447
|
+
"content" => content,
|
|
448
|
+
"language" => language,
|
|
449
|
+
"line_numbers" => nil,
|
|
471
450
|
}
|
|
472
451
|
block.content = content
|
|
473
452
|
|
|
@@ -475,9 +454,9 @@ module Prosereflect
|
|
|
475
454
|
end
|
|
476
455
|
|
|
477
456
|
def extract_language(html_node)
|
|
478
|
-
return nil unless html_node[
|
|
457
|
+
return nil unless html_node["class"]
|
|
479
458
|
|
|
480
|
-
return unless html_node[
|
|
459
|
+
return unless html_node["class"] =~ /language-(\w+)/
|
|
481
460
|
|
|
482
461
|
Regexp.last_match(1)
|
|
483
462
|
end
|
|
@@ -493,10 +472,10 @@ module Prosereflect
|
|
|
493
472
|
# Create a user mention node from HTML user-mention element
|
|
494
473
|
def create_user_node(html_node)
|
|
495
474
|
# Skip user mentions without data-id
|
|
496
|
-
return nil unless html_node[
|
|
475
|
+
return nil unless html_node["data-id"]
|
|
497
476
|
|
|
498
477
|
user = User.new
|
|
499
|
-
user.id = html_node[
|
|
478
|
+
user.id = html_node["data-id"]
|
|
500
479
|
user
|
|
501
480
|
end
|
|
502
481
|
end
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'node'
|
|
4
|
-
require_relative 'paragraph'
|
|
5
|
-
require_relative 'text'
|
|
6
|
-
require_relative 'hard_break'
|
|
7
|
-
|
|
8
3
|
module Prosereflect
|
|
9
4
|
# ListItem class represents a list item in ProseMirror.
|
|
10
5
|
class ListItem < Node
|
|
11
|
-
PM_TYPE =
|
|
6
|
+
PM_TYPE = "list_item"
|
|
12
7
|
|
|
13
|
-
attribute :type, :string, default: -> {
|
|
8
|
+
attribute :type, :string, default: -> {
|
|
9
|
+
self.class.send(:const_get, "PM_TYPE")
|
|
10
|
+
}
|
|
14
11
|
attribute :attrs, :hash
|
|
15
12
|
|
|
16
13
|
key_value do
|
|
17
|
-
map
|
|
18
|
-
map
|
|
19
|
-
map
|
|
14
|
+
map "type", to: :type, render_default: true
|
|
15
|
+
map "attrs", to: :attrs
|
|
16
|
+
map "content", to: :content
|
|
20
17
|
end
|
|
21
18
|
|
|
22
19
|
def initialize(attributes = {})
|
|
@@ -57,9 +54,11 @@ module Prosereflect
|
|
|
57
54
|
|
|
58
55
|
# Get plain text content from all nodes
|
|
59
56
|
def text_content
|
|
60
|
-
return
|
|
57
|
+
return "" unless content
|
|
61
58
|
|
|
62
|
-
content.map
|
|
59
|
+
content.map do |node|
|
|
60
|
+
node.respond_to?(:text_content) ? node.text_content : ""
|
|
61
|
+
end.join("\n").strip
|
|
63
62
|
end
|
|
64
63
|
end
|
|
65
64
|
end
|
|
@@ -1,47 +1,45 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'lutaml/model'
|
|
4
|
-
|
|
5
3
|
module Prosereflect
|
|
6
4
|
module Mark
|
|
7
5
|
class Base < Lutaml::Model::Serializable
|
|
8
|
-
PM_TYPE =
|
|
6
|
+
PM_TYPE = "mark"
|
|
9
7
|
|
|
10
8
|
attribute :type, :string, default: lambda {
|
|
11
9
|
begin
|
|
12
10
|
self.class.const_get(:PM_TYPE)
|
|
13
11
|
rescue StandardError
|
|
14
|
-
|
|
12
|
+
"mark"
|
|
15
13
|
end
|
|
16
14
|
}
|
|
17
15
|
attribute :attrs, :hash
|
|
18
16
|
|
|
19
17
|
key_value do
|
|
20
|
-
map
|
|
21
|
-
map
|
|
18
|
+
map "type", to: :type, render_default: true
|
|
19
|
+
map "attrs", to: :attrs
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
def self.create(attrs = nil)
|
|
25
23
|
new(type: const_get(:PM_TYPE), attrs: attrs)
|
|
26
24
|
rescue NameError
|
|
27
|
-
new(type:
|
|
25
|
+
new(type: "mark", attrs: attrs)
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
# Convert to hash for serialization
|
|
31
29
|
def to_h
|
|
32
|
-
result = {
|
|
33
|
-
result[
|
|
30
|
+
result = { "type" => type }
|
|
31
|
+
result["attrs"] = attrs if attrs && !attrs.empty?
|
|
34
32
|
result
|
|
35
33
|
end
|
|
36
34
|
|
|
37
35
|
# Override initialize to ensure the type is set correctly
|
|
38
36
|
def initialize(options = {})
|
|
39
|
-
super
|
|
37
|
+
super
|
|
40
38
|
# Only set the type to PM_TYPE if no type was provided in options
|
|
41
39
|
self.type = begin
|
|
42
40
|
options[:type] || self.class.const_get(:PM_TYPE)
|
|
43
41
|
rescue StandardError
|
|
44
|
-
|
|
42
|
+
"mark"
|
|
45
43
|
end
|
|
46
44
|
end
|
|
47
45
|
end
|