coradoc-html 1.1.7

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 (124) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/lib/coradoc/html/base.rb +157 -0
  4. data/lib/coradoc/html/config.rb +467 -0
  5. data/lib/coradoc/html/converter_base.rb +177 -0
  6. data/lib/coradoc/html/converters/admonition.rb +180 -0
  7. data/lib/coradoc/html/converters/attribute.rb +68 -0
  8. data/lib/coradoc/html/converters/attribute_reference.rb +60 -0
  9. data/lib/coradoc/html/converters/audio.rb +165 -0
  10. data/lib/coradoc/html/converters/base.rb +615 -0
  11. data/lib/coradoc/html/converters/bibliography.rb +82 -0
  12. data/lib/coradoc/html/converters/bibliography_entry.rb +108 -0
  13. data/lib/coradoc/html/converters/block_image.rb +72 -0
  14. data/lib/coradoc/html/converters/bold.rb +34 -0
  15. data/lib/coradoc/html/converters/break.rb +32 -0
  16. data/lib/coradoc/html/converters/comment_block.rb +42 -0
  17. data/lib/coradoc/html/converters/comment_line.rb +54 -0
  18. data/lib/coradoc/html/converters/cross_reference.rb +59 -0
  19. data/lib/coradoc/html/converters/document.rb +108 -0
  20. data/lib/coradoc/html/converters/example.rb +114 -0
  21. data/lib/coradoc/html/converters/highlight.rb +34 -0
  22. data/lib/coradoc/html/converters/include.rb +68 -0
  23. data/lib/coradoc/html/converters/inline_image.rb +41 -0
  24. data/lib/coradoc/html/converters/italic.rb +34 -0
  25. data/lib/coradoc/html/converters/line_break.rb +31 -0
  26. data/lib/coradoc/html/converters/link.rb +46 -0
  27. data/lib/coradoc/html/converters/list_item.rb +75 -0
  28. data/lib/coradoc/html/converters/listing.rb +99 -0
  29. data/lib/coradoc/html/converters/literal.rb +102 -0
  30. data/lib/coradoc/html/converters/monospace.rb +34 -0
  31. data/lib/coradoc/html/converters/open.rb +78 -0
  32. data/lib/coradoc/html/converters/ordered.rb +53 -0
  33. data/lib/coradoc/html/converters/paragraph.rb +46 -0
  34. data/lib/coradoc/html/converters/quote.rb +113 -0
  35. data/lib/coradoc/html/converters/reviewer_comment.rb +74 -0
  36. data/lib/coradoc/html/converters/reviewer_note.rb +134 -0
  37. data/lib/coradoc/html/converters/section.rb +90 -0
  38. data/lib/coradoc/html/converters/sidebar.rb +113 -0
  39. data/lib/coradoc/html/converters/source.rb +137 -0
  40. data/lib/coradoc/html/converters/source_code.rb +16 -0
  41. data/lib/coradoc/html/converters/span.rb +61 -0
  42. data/lib/coradoc/html/converters/strikethrough.rb +34 -0
  43. data/lib/coradoc/html/converters/subscript.rb +34 -0
  44. data/lib/coradoc/html/converters/superscript.rb +34 -0
  45. data/lib/coradoc/html/converters/table.rb +85 -0
  46. data/lib/coradoc/html/converters/table_cell.rb +203 -0
  47. data/lib/coradoc/html/converters/table_row.rb +45 -0
  48. data/lib/coradoc/html/converters/template_html_converter.rb +105 -0
  49. data/lib/coradoc/html/converters/term.rb +58 -0
  50. data/lib/coradoc/html/converters/text_element.rb +44 -0
  51. data/lib/coradoc/html/converters/underline.rb +34 -0
  52. data/lib/coradoc/html/converters/unordered.rb +47 -0
  53. data/lib/coradoc/html/converters/verse.rb +105 -0
  54. data/lib/coradoc/html/converters/video.rb +179 -0
  55. data/lib/coradoc/html/element_mapping.rb +210 -0
  56. data/lib/coradoc/html/entity.rb +137 -0
  57. data/lib/coradoc/html/input/cleaner.rb +163 -0
  58. data/lib/coradoc/html/input/config.rb +79 -0
  59. data/lib/coradoc/html/input/converters/a.rb +90 -0
  60. data/lib/coradoc/html/input/converters/aside.rb +23 -0
  61. data/lib/coradoc/html/input/converters/audio.rb +50 -0
  62. data/lib/coradoc/html/input/converters/base.rb +116 -0
  63. data/lib/coradoc/html/input/converters/blockquote.rb +25 -0
  64. data/lib/coradoc/html/input/converters/br.rb +19 -0
  65. data/lib/coradoc/html/input/converters/bypass.rb +83 -0
  66. data/lib/coradoc/html/input/converters/code.rb +25 -0
  67. data/lib/coradoc/html/input/converters/div.rb +25 -0
  68. data/lib/coradoc/html/input/converters/dl.rb +106 -0
  69. data/lib/coradoc/html/input/converters/drop.rb +28 -0
  70. data/lib/coradoc/html/input/converters/em.rb +23 -0
  71. data/lib/coradoc/html/input/converters/figure.rb +58 -0
  72. data/lib/coradoc/html/input/converters/h.rb +76 -0
  73. data/lib/coradoc/html/input/converters/head.rb +30 -0
  74. data/lib/coradoc/html/input/converters/hr.rb +20 -0
  75. data/lib/coradoc/html/input/converters/ignore.rb +22 -0
  76. data/lib/coradoc/html/input/converters/img.rb +110 -0
  77. data/lib/coradoc/html/input/converters/li.rb +35 -0
  78. data/lib/coradoc/html/input/converters/mark.rb +21 -0
  79. data/lib/coradoc/html/input/converters/markup.rb +107 -0
  80. data/lib/coradoc/html/input/converters/math.rb +46 -0
  81. data/lib/coradoc/html/input/converters/ol.rb +46 -0
  82. data/lib/coradoc/html/input/converters/p.rb +81 -0
  83. data/lib/coradoc/html/input/converters/pass_through.rb +19 -0
  84. data/lib/coradoc/html/input/converters/pre.rb +59 -0
  85. data/lib/coradoc/html/input/converters/q.rb +24 -0
  86. data/lib/coradoc/html/input/converters/strong.rb +22 -0
  87. data/lib/coradoc/html/input/converters/sub.rb +40 -0
  88. data/lib/coradoc/html/input/converters/sup.rb +40 -0
  89. data/lib/coradoc/html/input/converters/table.rb +64 -0
  90. data/lib/coradoc/html/input/converters/td.rb +70 -0
  91. data/lib/coradoc/html/input/converters/text.rb +67 -0
  92. data/lib/coradoc/html/input/converters/th.rb +20 -0
  93. data/lib/coradoc/html/input/converters/tr.rb +28 -0
  94. data/lib/coradoc/html/input/converters/video.rb +53 -0
  95. data/lib/coradoc/html/input/converters.rb +122 -0
  96. data/lib/coradoc/html/input/errors.rb +22 -0
  97. data/lib/coradoc/html/input/html_converter.rb +170 -0
  98. data/lib/coradoc/html/input/plugin.rb +169 -0
  99. data/lib/coradoc/html/input/plugins/plateau.rb +229 -0
  100. data/lib/coradoc/html/input/postprocessor.rb +31 -0
  101. data/lib/coradoc/html/input.rb +68 -0
  102. data/lib/coradoc/html/output.rb +95 -0
  103. data/lib/coradoc/html/renderer.rb +409 -0
  104. data/lib/coradoc/html/spa.rb +309 -0
  105. data/lib/coradoc/html/static.rb +293 -0
  106. data/lib/coradoc/html/template_config.rb +151 -0
  107. data/lib/coradoc/html/template_helpers.rb +58 -0
  108. data/lib/coradoc/html/template_locator.rb +114 -0
  109. data/lib/coradoc/html/theme/base.rb +231 -0
  110. data/lib/coradoc/html/theme/classic_renderer.rb +390 -0
  111. data/lib/coradoc/html/theme/modern/components/ui_components.rb +344 -0
  112. data/lib/coradoc/html/theme/modern/css_generator.rb +311 -0
  113. data/lib/coradoc/html/theme/modern/javascript_generator.rb +314 -0
  114. data/lib/coradoc/html/theme/modern/serializers/document_serializer.rb +382 -0
  115. data/lib/coradoc/html/theme/modern/tailwind_config_builder.rb +164 -0
  116. data/lib/coradoc/html/theme/modern/vue_template_generator.rb +374 -0
  117. data/lib/coradoc/html/theme/modern_renderer.rb +250 -0
  118. data/lib/coradoc/html/theme/registry.rb +153 -0
  119. data/lib/coradoc/html/theme.rb +13 -0
  120. data/lib/coradoc/html/transform/from_core_model.rb +32 -0
  121. data/lib/coradoc/html/transform/to_core_model.rb +39 -0
  122. data/lib/coradoc/html/version.rb +7 -0
  123. data/lib/coradoc/html.rb +255 -0
  124. metadata +264 -0
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for CoreModel::Block (example) to HTML <div class="example">
7
+ class Example < Base
8
+ # Convert CoreModel::Block (example) to HTML <div class="example">
9
+ def self.to_html(example, _options = {})
10
+ return '' unless example
11
+
12
+ # Build div attributes
13
+ attrs = build_attributes(example)
14
+
15
+ # Build title if present
16
+ title_html = build_title(example)
17
+
18
+ # Process example content
19
+ content = process_content(example.content)
20
+
21
+ # Combine title and content
22
+ example_html = ''
23
+ example_html += "#{title_html}\n" if title_html
24
+ example_html += content
25
+
26
+ %(<div#{attrs}>\n#{example_html}\n</div>)
27
+ end
28
+
29
+ # Convert HTML <div class="example"> to CoreModel::Block (example)
30
+ def self.to_coradoc(element, _options = {})
31
+ return nil unless element.name == 'div'
32
+ return nil unless element['class']&.include?('example')
33
+
34
+ # Extract title if present
35
+ title_elem = element.at_css('.example-title')
36
+ title = title_elem&.text&.strip
37
+
38
+ # Extract content - all children except title
39
+ content_nodes = if title_elem
40
+ element.children.reject { |node| node == title_elem }
41
+ else
42
+ element.children
43
+ end
44
+
45
+ content = extract_content(content_nodes)
46
+
47
+ # Extract ID if present
48
+ id = element['id']
49
+
50
+ Coradoc::CoreModel::ExampleBlock.new(
51
+ content: content,
52
+ title: title,
53
+ id: id
54
+ )
55
+ end
56
+
57
+ def self.build_attributes(example)
58
+ attrs = [%( class="example")]
59
+
60
+ # Add ID if present
61
+ attrs << %( id="#{escape_attribute(example.id)}") if example.id
62
+
63
+ attrs.join
64
+ end
65
+
66
+ def self.build_title(example)
67
+ return nil unless example.title
68
+
69
+ title_text = example.title.to_s
70
+ return nil if title_text.empty?
71
+
72
+ %(<div class="example-title">#{escape_html(title_text)}</div>)
73
+ end
74
+
75
+ def self.process_content(content)
76
+ return '' if content.nil?
77
+
78
+ if content.is_a?(Array)
79
+ content.map { |item| convert_item(item) }.join("\n")
80
+ elsif content.is_a?(String)
81
+ "<p>#{escape_html(content)}</p>"
82
+ else
83
+ convert_item(content)
84
+ end
85
+ end
86
+
87
+ def self.convert_item(item)
88
+ case item
89
+ when String
90
+ "<p>#{escape_html(item)}</p>"
91
+ else
92
+ convert_content_to_html(item)
93
+ end
94
+ end
95
+
96
+ def self.extract_content(nodes)
97
+ # Extract and convert content nodes
98
+ nodes.map do |node|
99
+ if node.text? && !node.text.strip.empty?
100
+ node.text.strip
101
+ elsif node.element?
102
+ case node.name
103
+ when 'p'
104
+ Paragraph.to_coradoc(node)
105
+ else
106
+ node.text.strip
107
+ end
108
+ end
109
+ end.compact
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for Highlight inline element
7
+ class Highlight < Base
8
+ class << self
9
+ # Convert HTML <mark> to CoreModel::InlineElement
10
+ # @param node [Nokogiri::XML::Node] HTML node
11
+ # @param state [Hash] Conversion state
12
+ # @return [Coradoc::CoreModel::InlineElement] Highlight inline element
13
+ def to_coradoc(node, state = {})
14
+ content = treat_children(node, state)
15
+ Coradoc::CoreModel::InlineElement.new(
16
+ format_type: 'highlight',
17
+ content: content
18
+ )
19
+ end
20
+
21
+ # Convert CoreModel::InlineElement (highlight) to HTML <mark>
22
+ # @param model [Coradoc::CoreModel::InlineElement] Highlight model
23
+ # @param state [Hash] Conversion state
24
+ # @return [String] HTML string
25
+ def to_html(model, state = {})
26
+ content = convert_content_to_html(model.content, state)
27
+ attributes = extract_model_attributes(model)
28
+ build_element('mark', content, attributes)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for include directives
7
+ class Include < Base
8
+ # Convert CoreModel::Block (include) to HTML comment with include directive
9
+ # Note: HTML doesn't have native include support, so we use a comment
10
+ def self.to_html(include_directive, _options = {})
11
+ return '' unless include_directive
12
+
13
+ # Get include path from metadata
14
+ path = include_directive.metadata&.dig(:path) || ''
15
+ path = escape_html(path)
16
+
17
+ # Build include directive as comment
18
+ comment_text = "include::#{path}[]"
19
+
20
+ # Add attributes if present
21
+ attrs = include_directive.metadata&.dig(:attributes) || {}
22
+ if attrs && !attrs.empty?
23
+ attrs_str = attrs.map { |k, v| "#{k}=#{v}" }.join(',')
24
+ comment_text = "include::#{path}[#{attrs_str}]" unless attrs_str.empty?
25
+ end
26
+
27
+ "<!-- #{comment_text} -->"
28
+ end
29
+
30
+ # Convert HTML comment with include directive to CoreModel::Block (include)
31
+ def self.to_coradoc(element, _options = {})
32
+ return nil unless element.comment?
33
+
34
+ # Check if comment contains include directive
35
+ text = element.text.to_s.strip
36
+ return nil unless text.match?(/^include::/)
37
+
38
+ # Parse include directive
39
+ # Format: include::path[attributes]
40
+ return unless text =~ /^include::([^\[]+)(\[([^\]]*)\])?/
41
+
42
+ path = ::Regexp.last_match(1).strip
43
+ attrs_str = ::Regexp.last_match(3)
44
+
45
+ attrs = {}
46
+ if attrs_str && !attrs_str.empty?
47
+ # Parse attributes (simplified - doesn't handle complex cases)
48
+ attrs_str.split(',').each do |attr|
49
+ if attr.include?('=')
50
+ k, v = attr.split('=', 2)
51
+ attrs[k.strip.to_sym] = v.strip
52
+ end
53
+ end
54
+ end
55
+
56
+ Coradoc::CoreModel::Block.new(
57
+ element_type: 'include',
58
+ content: path,
59
+ metadata: {
60
+ path: path,
61
+ attributes: attrs
62
+ }
63
+ )
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for CoreModel::Image (inline image)
7
+ class InlineImage < Base
8
+ def self.to_html(model, _state = {})
9
+ attrs = {}
10
+ # Remove leading colons from src (source format syntax artifact)
11
+ src = model.src
12
+ src = src.sub(/^:+/, '') if src
13
+ attrs[:src] = src if src
14
+ attrs[:id] = model.id if model.id
15
+ attrs[:alt] = model.alt || ''
16
+
17
+ # Extract additional attributes
18
+ attrs[:width] = model.width if model.width
19
+ attrs[:height] = model.height if model.height
20
+
21
+ build_element('img', nil, attrs)
22
+ end
23
+
24
+ def self.to_coradoc(node, _state = {})
25
+ return nil unless node.name == 'img'
26
+
27
+ attrs = extract_attributes(node)
28
+
29
+ Coradoc::CoreModel::Image.new(
30
+ src: attrs[:src],
31
+ id: attrs[:id],
32
+ alt: attrs[:alt],
33
+ width: attrs[:width],
34
+ height: attrs[:height],
35
+ inline: true
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for Italic inline element
7
+ class Italic < Base
8
+ class << self
9
+ # Convert HTML <em> or <i> to CoreModel::InlineElement
10
+ # @param node [Nokogiri::XML::Node] HTML node
11
+ # @param state [Hash] Conversion state
12
+ # @return [Coradoc::CoreModel::InlineElement] Italic inline element
13
+ def to_coradoc(node, state = {})
14
+ content = treat_children(node, state)
15
+ Coradoc::CoreModel::InlineElement.new(
16
+ format_type: 'italic',
17
+ content: content
18
+ )
19
+ end
20
+
21
+ # Convert CoreModel::InlineElement (italic) to HTML <em>
22
+ # @param model [Coradoc::CoreModel::InlineElement] Italic model
23
+ # @param state [Hash] Conversion state
24
+ # @return [String] HTML string
25
+ def to_html(model, state = {})
26
+ content = convert_content_to_html(model.content, state)
27
+ attributes = extract_model_attributes(model)
28
+ build_element('em', content, attributes)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for LineBreak inline element
7
+ class LineBreak < Base
8
+ class << self
9
+ # Convert HTML <br> to CoreModel::InlineElement (break)
10
+ # @param node [Nokogiri::XML::Node] HTML node
11
+ # @param state [Hash] Conversion state
12
+ # @return [Coradoc::CoreModel::InlineElement] LineBreak inline element
13
+ def to_coradoc(_node, _state = {})
14
+ Coradoc::CoreModel::InlineElement.new(
15
+ format_type: 'break',
16
+ metadata: { break_type: 'line' }
17
+ )
18
+ end
19
+
20
+ # Convert CoreModel::InlineElement (break with line type) to HTML <br>
21
+ # @param model [Coradoc::CoreModel::InlineElement] LineBreak model
22
+ # @param state [Hash] Conversion state
23
+ # @return [String] HTML string
24
+ def to_html(_model, _state = {})
25
+ build_element('br')
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ class Link < Base
7
+ def self.to_html(model, _state = {})
8
+ # Handle CoreModel::InlineElement with format_type "link"
9
+ attrs = {}
10
+ attrs[:href] = model.target if model.target
11
+ attrs[:id] = model.id if model.id
12
+
13
+ # Get title from metadata
14
+ if model.metadata && model.metadata[:title]
15
+ attrs[:title] =
16
+ model.metadata[:title]
17
+ end
18
+
19
+ # Determine link text - use content or target
20
+ text = model.content || model.target || ''
21
+ build_element('a', text, attrs)
22
+ end
23
+
24
+ def self.to_coradoc(node, _state = {})
25
+ return nil unless node.name == 'a'
26
+
27
+ attrs = extract_attributes(node)
28
+ href = attrs[:href] || ''
29
+ title = attrs[:title]
30
+ text = node.text
31
+
32
+ # If text equals href, don't set text (empty link text)
33
+ text = nil if text == href
34
+
35
+ Coradoc::CoreModel::InlineElement.new(
36
+ format_type: 'link',
37
+ target: href,
38
+ content: text,
39
+ metadata: { title: title }.compact,
40
+ id: attrs[:id]
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for ListItem
7
+ class ListItem < Base
8
+ class << self
9
+ # Convert HTML <li> to CoreModel::ListItem
10
+ # @param node [Nokogiri::XML::Node] HTML node
11
+ # @param state [Hash] Conversion state
12
+ # @return [Coradoc::CoreModel::ListItem] ListItem model
13
+ def to_coradoc(node, state = {})
14
+ attrs = extract_node_attributes(node)
15
+
16
+ item = Coradoc::CoreModel::ListItem.new
17
+ item.id = attrs[:id] if attrs[:id]
18
+
19
+ # Extract content and nested lists
20
+ content_nodes = []
21
+ nested_list = nil
22
+
23
+ node.children.each do |child|
24
+ case child.name
25
+ when 'ul', 'ol'
26
+ # This is a nested list
27
+ nested_list = convert_node_to_core(child, state)
28
+ else
29
+ content_nodes << child unless child.text.strip.empty? && child.name == 'text'
30
+ end
31
+ end
32
+
33
+ # Convert content nodes - collect as mixed content array
34
+ if content_nodes.any?
35
+ content = content_nodes.flat_map { |n| convert_node_to_core(n, state) }.compact
36
+ # Store as children for mixed content
37
+ item.children = content if content.any?
38
+ end
39
+
40
+ item.nested = nested_list if nested_list
41
+
42
+ item
43
+ end
44
+
45
+ # Convert CoreModel::ListItem to HTML <li>
46
+ # @param model [Coradoc::CoreModel::ListItem] ListItem model
47
+ # @param state [Hash] Conversion state
48
+ # @return [String] HTML string
49
+ def to_html(model, state = {})
50
+ parts = []
51
+
52
+ # Convert main content - check children first (mixed content), then content
53
+ content_to_render = model.children&.any? ? model.children : model.content
54
+ parts << convert_content_to_html(content_to_render, state) if content_to_render
55
+
56
+ # Convert attached content
57
+ if model.attached && !model.attached.empty?
58
+ model.attached.each do |attached_item|
59
+ parts << convert_content_to_html(attached_item, state)
60
+ end
61
+ end
62
+
63
+ # Convert nested list
64
+ parts << convert_content_to_html(model.nested, state) if model.nested
65
+
66
+ attrs = {}
67
+ attrs[:id] = model.id if model.id
68
+
69
+ build_element('li', parts.join("\n"), attrs)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for CoreModel::Block (listing) to HTML <pre>
7
+ class Listing < Base
8
+ # Convert CoreModel::Block (listing) to HTML <pre>
9
+ def self.to_html(listing, _options = {})
10
+ return '' unless listing
11
+
12
+ # Build pre attributes
13
+ attrs = build_attributes(listing)
14
+
15
+ # Build title if present
16
+ title_html = build_title(listing)
17
+
18
+ # Process listing content - preserve formatting
19
+ content = process_content(listing.content)
20
+
21
+ # Combine title and content
22
+ listing_html = ''
23
+ listing_html += "#{title_html}\n" if title_html
24
+ listing_html += %(<pre#{attrs}>#{content}</pre>)
25
+
26
+ if title_html
27
+ %(<div class="listing-block">\n#{listing_html}\n</div>)
28
+ else
29
+ listing_html
30
+ end
31
+ end
32
+
33
+ # Convert HTML <pre> to CoreModel::Block (listing)
34
+ def self.to_coradoc(element, _options = {})
35
+ # Handle both <pre> and <div class="listing-block"><pre>
36
+ pre_elem = if element.name == 'div' && element['class']&.include?('listing-block')
37
+ element.at_css('pre')
38
+ elsif element.name == 'pre'
39
+ element
40
+ else
41
+ return nil
42
+ end
43
+
44
+ return nil unless pre_elem
45
+
46
+ # Extract title if in listing-block wrapper
47
+ title = if element.name == 'div'
48
+ title_elem = element.at_css('.listing-title')
49
+ title_elem&.text&.strip
50
+ end
51
+
52
+ # Extract content
53
+ content = pre_elem.text
54
+
55
+ # Extract ID if present
56
+ id = pre_elem['id'] || element['id']
57
+
58
+ Coradoc::CoreModel::SourceBlock.new(
59
+ content: content,
60
+ title: title,
61
+ id: id
62
+ )
63
+ end
64
+
65
+ def self.build_attributes(listing)
66
+ attrs = [%( class="listing")]
67
+
68
+ # Add ID if present
69
+ attrs << %( id="#{escape_attribute(listing.id)}") if listing.id
70
+
71
+ attrs.join
72
+ end
73
+
74
+ def self.build_title(listing)
75
+ return nil unless listing.title
76
+
77
+ title_text = listing.title.to_s
78
+ return nil if title_text.empty?
79
+
80
+ %(<div class="listing-title">#{escape_html(title_text)}</div>)
81
+ end
82
+
83
+ def self.process_content(content)
84
+ return '' if content.nil?
85
+
86
+ # For listing, preserve the content as-is
87
+ if content.is_a?(String)
88
+ escape_html(content)
89
+ elsif content.is_a?(Array)
90
+ # Join array items with newlines
91
+ content.map { |line| escape_html(line.to_s) }.join("\n")
92
+ else
93
+ escape_html(content.to_s)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for CoreModel::Block (literal) to HTML <pre>
7
+ class Literal < Base
8
+ # Convert CoreModel::Block (literal) to HTML <pre>
9
+ def self.to_html(literal, _options = {})
10
+ return '' unless literal
11
+
12
+ # Build pre attributes
13
+ attrs = build_attributes(literal)
14
+
15
+ # Build title if present
16
+ title_html = build_title(literal)
17
+
18
+ # Process literal content - preserve exact formatting
19
+ content = process_content(literal.content)
20
+
21
+ # Combine title and content
22
+ literal_html = ''
23
+ literal_html += "#{title_html}\n" if title_html
24
+ literal_html += %(<pre#{attrs}>#{content}</pre>)
25
+
26
+ if title_html
27
+ %(<div class="literal-block">\n#{literal_html}\n</div>)
28
+ else
29
+ literal_html
30
+ end
31
+ end
32
+
33
+ # Convert HTML <pre> to CoreModel::Block (literal)
34
+ def self.to_coradoc(element, _options = {})
35
+ # Handle both <pre> and <div class="literal-block"><pre>
36
+ pre_elem = if element.name == 'div' && element['class']&.include?('literal-block')
37
+ element.at_css('pre')
38
+ elsif element.name == 'pre'
39
+ # Only convert if it's a literal (no code class)
40
+ return nil if element['class']&.include?('code')
41
+
42
+ element
43
+ else
44
+ return nil
45
+ end
46
+
47
+ return nil unless pre_elem
48
+
49
+ # Extract title if in literal-block wrapper
50
+ title = if element.name == 'div'
51
+ title_elem = element.at_css('.literal-title')
52
+ title_elem&.text&.strip
53
+ end
54
+
55
+ # Extract content
56
+ content = pre_elem.text
57
+
58
+ # Extract ID if present
59
+ id = pre_elem['id'] || element['id']
60
+
61
+ Coradoc::CoreModel::LiteralBlock.new(
62
+ content: content,
63
+ title: title,
64
+ id: id
65
+ )
66
+ end
67
+
68
+ def self.build_attributes(literal)
69
+ attrs = [%( class="literal")]
70
+
71
+ # Add ID if present
72
+ attrs << %( id="#{escape_attribute(literal.id)}") if literal.id
73
+
74
+ attrs.join
75
+ end
76
+
77
+ def self.build_title(literal)
78
+ return nil unless literal.title
79
+
80
+ title_text = literal.title.to_s
81
+ return nil if title_text.empty?
82
+
83
+ %(<div class="literal-title">#{escape_html(title_text)}</div>)
84
+ end
85
+
86
+ def self.process_content(content)
87
+ return '' if content.nil?
88
+
89
+ # For literal, preserve the content exactly as-is
90
+ if content.is_a?(String)
91
+ escape_html(content)
92
+ elsif content.is_a?(Array)
93
+ # Join array items with newlines
94
+ content.map { |line| escape_html(line.to_s) }.join("\n")
95
+ else
96
+ escape_html(content.to_s)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Html
5
+ module Converters
6
+ # Converter for Monospace inline element
7
+ class Monospace < Base
8
+ class << self
9
+ # Convert HTML <code> to CoreModel::InlineElement
10
+ # @param node [Nokogiri::XML::Node] HTML node
11
+ # @param state [Hash] Conversion state
12
+ # @return [Coradoc::CoreModel::InlineElement] Monospace inline element
13
+ def to_coradoc(node, state = {})
14
+ content = treat_children(node, state)
15
+ Coradoc::CoreModel::InlineElement.new(
16
+ format_type: 'monospace',
17
+ content: content
18
+ )
19
+ end
20
+
21
+ # Convert CoreModel::InlineElement (monospace) to HTML <code>
22
+ # @param model [Coradoc::CoreModel::InlineElement] Monospace model
23
+ # @param state [Hash] Conversion state
24
+ # @return [String] HTML string
25
+ def to_html(model, state = {})
26
+ content = convert_content_to_html(model.content, state)
27
+ attributes = extract_model_attributes(model)
28
+ build_element('code', content, attributes)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end