coradoc-html 1.1.7 → 1.1.13
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/lib/coradoc/html/config.rb +36 -12
- data/lib/coradoc/html/converter_base.rb +26 -68
- data/lib/coradoc/html/drop/annotation_drop.rb +31 -0
- data/lib/coradoc/html/drop/base.rb +77 -0
- data/lib/coradoc/html/drop/bibliography_drop.rb +15 -0
- data/lib/coradoc/html/drop/bibliography_entry_drop.rb +24 -0
- data/lib/coradoc/html/drop/block_drop.rb +69 -0
- data/lib/coradoc/html/drop/definition_item_drop.rb +36 -0
- data/lib/coradoc/html/drop/definition_list_drop.rb +15 -0
- data/lib/coradoc/html/drop/document_drop.rb +52 -0
- data/lib/coradoc/html/drop/drop_factory.rb +73 -0
- data/lib/coradoc/html/drop/footnote_drop.rb +24 -0
- data/lib/coradoc/html/drop/image_drop.rb +35 -0
- data/lib/coradoc/html/drop/inline_element_drop.rb +64 -0
- data/lib/coradoc/html/drop/list_block_drop.rb +23 -0
- data/lib/coradoc/html/drop/list_item_drop.rb +20 -0
- data/lib/coradoc/html/drop/table_cell_drop.rb +35 -0
- data/lib/coradoc/html/drop/table_drop.rb +15 -0
- data/lib/coradoc/html/drop/table_row_drop.rb +23 -0
- data/lib/coradoc/html/drop/term_drop.rb +24 -0
- data/lib/coradoc/html/drop/text_content_drop.rb +15 -0
- data/lib/coradoc/html/drop/toc_drop.rb +15 -0
- data/lib/coradoc/html/drop/toc_entry_drop.rb +32 -0
- data/lib/coradoc/html/escape.rb +29 -0
- data/lib/coradoc/html/input/cleaner.rb +4 -33
- data/lib/coradoc/html/input/config.rb +4 -3
- data/lib/coradoc/html/input/converters/a.rb +8 -19
- data/lib/coradoc/html/input/converters/aside.rb +4 -5
- data/lib/coradoc/html/input/converters/audio.rb +8 -35
- data/lib/coradoc/html/input/converters/base.rb +29 -27
- data/lib/coradoc/html/input/converters/blockquote.rb +4 -2
- data/lib/coradoc/html/input/converters/br.rb +4 -4
- data/lib/coradoc/html/input/converters/bypass.rb +68 -67
- data/lib/coradoc/html/input/converters/code.rb +7 -5
- data/lib/coradoc/html/input/converters/div.rb +4 -4
- data/lib/coradoc/html/input/converters/dl.rb +3 -25
- data/lib/coradoc/html/input/converters/drop.rb +13 -13
- data/lib/coradoc/html/input/converters/em.rb +5 -3
- data/lib/coradoc/html/input/converters/figure.rb +3 -26
- data/lib/coradoc/html/input/converters/h.rb +9 -11
- data/lib/coradoc/html/input/converters/head.rb +5 -4
- data/lib/coradoc/html/input/converters/hr.rb +4 -5
- data/lib/coradoc/html/input/converters/img.rb +4 -9
- data/lib/coradoc/html/input/converters/li.rb +3 -1
- data/lib/coradoc/html/input/converters/mark.rb +3 -1
- data/lib/coradoc/html/input/converters/markup.rb +4 -8
- data/lib/coradoc/html/input/converters/math.rb +7 -14
- data/lib/coradoc/html/input/converters/media_base.rb +50 -0
- data/lib/coradoc/html/input/converters/ol.rb +6 -8
- data/lib/coradoc/html/input/converters/p.rb +43 -34
- data/lib/coradoc/html/input/converters/pass_through.rb +2 -4
- data/lib/coradoc/html/input/converters/positional_formatting.rb +37 -0
- data/lib/coradoc/html/input/converters/pre.rb +3 -3
- data/lib/coradoc/html/input/converters/q.rb +6 -3
- data/lib/coradoc/html/input/converters/strong.rb +4 -2
- data/lib/coradoc/html/input/converters/sub.rb +7 -23
- data/lib/coradoc/html/input/converters/sup.rb +7 -23
- data/lib/coradoc/html/input/converters/table.rb +3 -1
- data/lib/coradoc/html/input/converters/td.rb +4 -30
- data/lib/coradoc/html/input/converters/text.rb +4 -3
- data/lib/coradoc/html/input/converters/tr.rb +3 -2
- data/lib/coradoc/html/input/converters/video.rb +14 -36
- data/lib/coradoc/html/input/converters.rb +17 -35
- data/lib/coradoc/html/input/html_converter.rb +2 -74
- data/lib/coradoc/html/input/plugin.rb +8 -50
- data/lib/coradoc/html/input/plugins/plateau.rb +4 -19
- data/lib/coradoc/html/input/postprocessor.rb +3 -9
- data/lib/coradoc/html/input.rb +26 -8
- data/lib/coradoc/html/layout_renderer.rb +163 -0
- data/lib/coradoc/html/output.rb +6 -12
- data/lib/coradoc/html/renderer.rb +84 -350
- data/lib/coradoc/html/section_numberable.rb +9 -0
- data/lib/coradoc/html/spa.rb +29 -270
- data/lib/coradoc/html/static.rb +29 -238
- data/lib/coradoc/html/template_caching.rb +31 -0
- data/lib/coradoc/html/template_config.rb +11 -70
- data/lib/coradoc/html/template_helpers.rb +39 -31
- data/lib/coradoc/html/template_locator.rb +17 -11
- data/lib/coradoc/html/theme.rb +1 -7
- data/lib/coradoc/html/title_text.rb +57 -0
- data/lib/coradoc/html/toc_builder.rb +105 -0
- data/lib/coradoc/html/toc_serializer.rb +33 -0
- data/lib/coradoc/html/transform/from_core_model.rb +13 -12
- data/lib/coradoc/html/transform/to_core_model.rb +10 -12
- data/lib/coradoc/html/version.rb +1 -1
- data/lib/coradoc/html.rb +43 -88
- metadata +37 -70
- data/lib/coradoc/html/base.rb +0 -157
- data/lib/coradoc/html/converters/admonition.rb +0 -180
- data/lib/coradoc/html/converters/attribute.rb +0 -68
- data/lib/coradoc/html/converters/attribute_reference.rb +0 -60
- data/lib/coradoc/html/converters/audio.rb +0 -165
- data/lib/coradoc/html/converters/base.rb +0 -615
- data/lib/coradoc/html/converters/bibliography.rb +0 -82
- data/lib/coradoc/html/converters/bibliography_entry.rb +0 -108
- data/lib/coradoc/html/converters/block_image.rb +0 -72
- data/lib/coradoc/html/converters/bold.rb +0 -34
- data/lib/coradoc/html/converters/break.rb +0 -32
- data/lib/coradoc/html/converters/comment_block.rb +0 -42
- data/lib/coradoc/html/converters/comment_line.rb +0 -54
- data/lib/coradoc/html/converters/cross_reference.rb +0 -59
- data/lib/coradoc/html/converters/document.rb +0 -108
- data/lib/coradoc/html/converters/example.rb +0 -114
- data/lib/coradoc/html/converters/highlight.rb +0 -34
- data/lib/coradoc/html/converters/include.rb +0 -68
- data/lib/coradoc/html/converters/inline_image.rb +0 -41
- data/lib/coradoc/html/converters/italic.rb +0 -34
- data/lib/coradoc/html/converters/line_break.rb +0 -31
- data/lib/coradoc/html/converters/link.rb +0 -46
- data/lib/coradoc/html/converters/list_item.rb +0 -75
- data/lib/coradoc/html/converters/listing.rb +0 -99
- data/lib/coradoc/html/converters/literal.rb +0 -102
- data/lib/coradoc/html/converters/monospace.rb +0 -34
- data/lib/coradoc/html/converters/open.rb +0 -78
- data/lib/coradoc/html/converters/ordered.rb +0 -53
- data/lib/coradoc/html/converters/paragraph.rb +0 -46
- data/lib/coradoc/html/converters/quote.rb +0 -113
- data/lib/coradoc/html/converters/reviewer_comment.rb +0 -74
- data/lib/coradoc/html/converters/reviewer_note.rb +0 -134
- data/lib/coradoc/html/converters/section.rb +0 -90
- data/lib/coradoc/html/converters/sidebar.rb +0 -113
- data/lib/coradoc/html/converters/source.rb +0 -137
- data/lib/coradoc/html/converters/source_code.rb +0 -16
- data/lib/coradoc/html/converters/span.rb +0 -61
- data/lib/coradoc/html/converters/strikethrough.rb +0 -34
- data/lib/coradoc/html/converters/subscript.rb +0 -34
- data/lib/coradoc/html/converters/superscript.rb +0 -34
- data/lib/coradoc/html/converters/table.rb +0 -85
- data/lib/coradoc/html/converters/table_cell.rb +0 -203
- data/lib/coradoc/html/converters/table_row.rb +0 -45
- data/lib/coradoc/html/converters/template_html_converter.rb +0 -105
- data/lib/coradoc/html/converters/term.rb +0 -58
- data/lib/coradoc/html/converters/text_element.rb +0 -44
- data/lib/coradoc/html/converters/underline.rb +0 -34
- data/lib/coradoc/html/converters/unordered.rb +0 -47
- data/lib/coradoc/html/converters/verse.rb +0 -105
- data/lib/coradoc/html/converters/video.rb +0 -179
- data/lib/coradoc/html/element_mapping.rb +0 -210
- data/lib/coradoc/html/entity.rb +0 -137
- data/lib/coradoc/html/input/converters/ignore.rb +0 -22
- data/lib/coradoc/html/input/converters/th.rb +0 -20
- data/lib/coradoc/html/theme/base.rb +0 -231
- data/lib/coradoc/html/theme/classic_renderer.rb +0 -390
- data/lib/coradoc/html/theme/modern/components/ui_components.rb +0 -344
- data/lib/coradoc/html/theme/modern/css_generator.rb +0 -311
- data/lib/coradoc/html/theme/modern/javascript_generator.rb +0 -314
- data/lib/coradoc/html/theme/modern/serializers/document_serializer.rb +0 -382
- data/lib/coradoc/html/theme/modern/tailwind_config_builder.rb +0 -164
- data/lib/coradoc/html/theme/modern/vue_template_generator.rb +0 -374
- data/lib/coradoc/html/theme/modern_renderer.rb +0 -250
- data/lib/coradoc/html/theme/registry.rb +0 -153
data/lib/coradoc/html/spa.rb
CHANGED
|
@@ -1,308 +1,67 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'coradoc/html/renderer'
|
|
4
|
+
|
|
3
5
|
module Coradoc
|
|
4
6
|
module Html
|
|
5
7
|
autoload :ConverterBase, "#{__dir__}/converter_base"
|
|
6
8
|
|
|
7
|
-
# SPA
|
|
8
|
-
#
|
|
9
|
-
# Converts Coradoc::CoreModel::StructuralElement to a modern Vue.js + Tailwind CSS
|
|
10
|
-
# single-page application with glass morphism aesthetics.
|
|
11
|
-
#
|
|
12
|
-
# Features:
|
|
13
|
-
# - Vue.js 3 reactive components
|
|
14
|
-
# - Tailwind CSS styling
|
|
15
|
-
# - Glass morphism design
|
|
16
|
-
# - Dark/light theme toggle
|
|
17
|
-
# - Reading progress indicator
|
|
18
|
-
# - Sticky TOC sidebar
|
|
19
|
-
# - Copy code buttons
|
|
20
|
-
# - Smooth animations
|
|
21
|
-
#
|
|
22
|
-
# @example Basic usage
|
|
23
|
-
# doc = Coradoc.parse_file('document.adoc')
|
|
24
|
-
# html = Coradoc::Html::Spa.convert(doc)
|
|
9
|
+
# SPA HTML converter
|
|
25
10
|
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# theme_variant: :glass,
|
|
29
|
-
# primary_color: '#6366f1',
|
|
30
|
-
# theme_toggle: true,
|
|
31
|
-
# reading_progress: true
|
|
32
|
-
# )
|
|
33
|
-
# html = Coradoc::Html::Spa.convert(doc, config)
|
|
34
|
-
#
|
|
35
|
-
# @example Write to file
|
|
36
|
-
# Coradoc::Html::Spa.to_file(doc, 'output.html', config)
|
|
11
|
+
# Converts CoreModel documents to SPA HTML5 with embedded Vue.js frontend.
|
|
12
|
+
# Uses the unified Liquid template pipeline.
|
|
37
13
|
class Spa < ConverterBase
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# How to deliver assets (:embedded always for SPA)
|
|
43
|
-
attr_accessor :asset_delivery
|
|
44
|
-
|
|
45
|
-
# Theme appearance variant (:glass, :minimal, :vibrant)
|
|
46
|
-
attr_accessor :theme_variant
|
|
47
|
-
|
|
48
|
-
# Primary color (hex string, e.g., '#6366f1')
|
|
49
|
-
attr_accessor :primary_color
|
|
50
|
-
|
|
51
|
-
# Accent color (hex string, e.g., '#8b5cf6')
|
|
52
|
-
attr_accessor :accent_color
|
|
53
|
-
|
|
54
|
-
# Whether to enable theme toggle (dark/light mode)
|
|
55
|
-
attr_accessor :theme_toggle
|
|
56
|
-
|
|
57
|
-
# Whether to show reading progress bar
|
|
58
|
-
attr_accessor :reading_progress
|
|
59
|
-
|
|
60
|
-
# Whether to show back to top button
|
|
61
|
-
attr_accessor :back_to_top
|
|
62
|
-
|
|
63
|
-
# Whether TOC should be sticky
|
|
64
|
-
attr_accessor :toc_sticky
|
|
65
|
-
|
|
66
|
-
# Whether to add copy buttons to code blocks
|
|
67
|
-
attr_accessor :copy_code_buttons
|
|
68
|
-
|
|
69
|
-
# TOC levels to include (1-5)
|
|
70
|
-
attr_accessor :toc_levels
|
|
14
|
+
class Configuration < ConverterBase::ConfigurationBase
|
|
15
|
+
attr_accessor :theme_toggle, :reading_progress,
|
|
16
|
+
:toc_sticky, :toc_levels, :lang,
|
|
17
|
+
:template_dirs, :dist_dir
|
|
71
18
|
|
|
72
|
-
# TOC title text
|
|
73
|
-
attr_accessor :toc_title
|
|
74
|
-
|
|
75
|
-
# Whether to enable animations
|
|
76
|
-
attr_accessor :enable_animations
|
|
77
|
-
|
|
78
|
-
# Animation duration (CSS value, e.g., '300ms')
|
|
79
|
-
attr_accessor :animation_duration
|
|
80
|
-
|
|
81
|
-
# Whether to lazy load images
|
|
82
|
-
attr_accessor :lazy_load_images
|
|
83
|
-
|
|
84
|
-
# Maximum width of the container (CSS value)
|
|
85
|
-
attr_accessor :max_width
|
|
86
|
-
|
|
87
|
-
# Content width (CSS value)
|
|
88
|
-
attr_accessor :content_width
|
|
89
|
-
|
|
90
|
-
# Sidebar width for TOC (CSS value)
|
|
91
|
-
attr_accessor :sidebar_width
|
|
92
|
-
|
|
93
|
-
# Language attribute for HTML
|
|
94
|
-
attr_accessor :lang
|
|
95
|
-
|
|
96
|
-
# Custom meta description
|
|
97
|
-
attr_accessor :meta_description
|
|
98
|
-
|
|
99
|
-
# Custom meta keywords
|
|
100
|
-
attr_accessor :meta_keywords
|
|
101
|
-
|
|
102
|
-
# Enable Open Graph meta tags
|
|
103
|
-
attr_accessor :open_graph
|
|
104
|
-
|
|
105
|
-
# Valid theme variants
|
|
106
|
-
VALID_THEME_VARIANTS = %i[glass minimal vibrant].freeze
|
|
107
|
-
|
|
108
|
-
# Initialize configuration with options
|
|
109
|
-
#
|
|
110
|
-
# @param options [Hash] Configuration options
|
|
111
19
|
def initialize(**options)
|
|
112
|
-
@asset_delivery = options[:asset_delivery] || :embedded
|
|
113
|
-
@theme_variant = options[:theme_variant] || :glass
|
|
114
|
-
@primary_color = options[:primary_color] || '#6366f1'
|
|
115
|
-
@accent_color = options[:accent_color] || '#8b5cf6'
|
|
116
20
|
@theme_toggle = options.fetch(:theme_toggle, true)
|
|
117
21
|
@reading_progress = options.fetch(:reading_progress, true)
|
|
118
|
-
@back_to_top = options.fetch(:back_to_top, true)
|
|
119
22
|
@toc_sticky = options.fetch(:toc_sticky, true)
|
|
120
|
-
@copy_code_buttons = options.fetch(:copy_code_buttons, true)
|
|
121
23
|
@toc_levels = options[:toc_levels] || 2
|
|
122
|
-
@toc_title = options[:toc_title] || 'Table of Contents'
|
|
123
|
-
@enable_animations = options.fetch(:enable_animations, true)
|
|
124
|
-
@animation_duration = options[:animation_duration] || '300ms'
|
|
125
|
-
@lazy_load_images = options.fetch(:lazy_load_images, true)
|
|
126
|
-
@max_width = options[:max_width] || '1200px'
|
|
127
|
-
@content_width = options[:content_width] || '65ch'
|
|
128
|
-
@sidebar_width = options[:sidebar_width] || '280px'
|
|
129
24
|
@lang = options[:lang] || 'en'
|
|
130
|
-
@
|
|
131
|
-
@
|
|
132
|
-
@open_graph = options.fetch(:open_graph, false)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# Default configuration
|
|
136
|
-
#
|
|
137
|
-
# @return [Configuration] Default configuration instance
|
|
138
|
-
def self.defaults
|
|
139
|
-
new
|
|
25
|
+
@template_dirs = options[:template_dirs]
|
|
26
|
+
@dist_dir = options[:dist_dir]
|
|
140
27
|
end
|
|
141
28
|
|
|
142
|
-
# Merge with another configuration or hash
|
|
143
|
-
#
|
|
144
|
-
# @param other [Hash, Configuration] Configuration to merge
|
|
145
|
-
# @return [Configuration] New merged configuration
|
|
146
|
-
def merge(other)
|
|
147
|
-
other_hash = other.is_a?(Configuration) ? other.to_h : other.to_h.transform_keys(&:to_sym)
|
|
148
|
-
self.class.new(**to_h.merge(other_hash))
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# Convert to hash
|
|
152
|
-
#
|
|
153
|
-
# @return [Hash] Configuration as hash
|
|
154
29
|
def to_h
|
|
155
30
|
{
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
accent_color: @accent_color,
|
|
160
|
-
theme_toggle: @theme_toggle,
|
|
161
|
-
reading_progress: @reading_progress,
|
|
162
|
-
back_to_top: @back_to_top,
|
|
163
|
-
toc_sticky: @toc_sticky,
|
|
164
|
-
copy_code_buttons: @copy_code_buttons,
|
|
165
|
-
toc_levels: @toc_levels,
|
|
166
|
-
toc_title: @toc_title,
|
|
167
|
-
enable_animations: @enable_animations,
|
|
168
|
-
animation_duration: @animation_duration,
|
|
169
|
-
lazy_load_images: @lazy_load_images,
|
|
170
|
-
max_width: @max_width,
|
|
171
|
-
content_width: @content_width,
|
|
172
|
-
sidebar_width: @sidebar_width,
|
|
173
|
-
lang: @lang,
|
|
174
|
-
meta_description: @meta_description,
|
|
175
|
-
meta_keywords: @meta_keywords,
|
|
176
|
-
open_graph: @open_graph
|
|
31
|
+
theme_toggle: @theme_toggle, reading_progress: @reading_progress,
|
|
32
|
+
toc_sticky: @toc_sticky, toc_levels: @toc_levels,
|
|
33
|
+
lang: @lang, template_dirs: @template_dirs, dist_dir: @dist_dir
|
|
177
34
|
}
|
|
178
35
|
end
|
|
179
36
|
|
|
180
|
-
# Validate configuration
|
|
181
|
-
#
|
|
182
|
-
# @raise [ConverterBase::ValidationError] if configuration is invalid
|
|
183
37
|
def validate!
|
|
184
|
-
validate_hex_color(@primary_color, 'primary_color')
|
|
185
|
-
validate_hex_color(@accent_color, 'accent_color')
|
|
186
|
-
validate_css_value(@max_width, 'max_width')
|
|
187
|
-
validate_css_value(@content_width, 'content_width')
|
|
188
|
-
validate_css_value(@sidebar_width, 'sidebar_width')
|
|
189
|
-
validate_css_value(@animation_duration, 'animation_duration')
|
|
190
|
-
|
|
191
|
-
unless VALID_THEME_VARIANTS.include?(@theme_variant.to_sym)
|
|
192
|
-
raise ConverterBase::ValidationError,
|
|
193
|
-
"Invalid theme variant: #{@theme_variant}. " \
|
|
194
|
-
"Valid variants: #{VALID_THEME_VARIANTS.join(', ')}"
|
|
195
|
-
end
|
|
196
|
-
|
|
197
38
|
return if @toc_levels.is_a?(Integer) && @toc_levels.between?(1, 5)
|
|
198
39
|
|
|
199
40
|
raise ConverterBase::ValidationError,
|
|
200
41
|
'TOC levels must be an integer between 1 and 5'
|
|
201
42
|
end
|
|
202
|
-
|
|
203
|
-
# Convert to options hash for ModernRenderer
|
|
204
|
-
#
|
|
205
|
-
# @return [Hash] Options hash for the modern renderer
|
|
206
|
-
def to_renderer_options
|
|
207
|
-
{
|
|
208
|
-
modern: to_h,
|
|
209
|
-
lang: @lang,
|
|
210
|
-
toc: @toc_sticky,
|
|
211
|
-
toclevels: @toc_levels,
|
|
212
|
-
toc_title: @toc_title
|
|
213
|
-
}
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
private
|
|
217
|
-
|
|
218
|
-
# Validate hex color format
|
|
219
|
-
#
|
|
220
|
-
# @param color [String] Color string to validate
|
|
221
|
-
# @param field_name [String] Field name for error message
|
|
222
|
-
# @raise [ConverterBase::ValidationError] if invalid
|
|
223
|
-
def validate_hex_color(color, field_name)
|
|
224
|
-
return unless color
|
|
225
|
-
|
|
226
|
-
return if color.match?(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/)
|
|
227
|
-
|
|
228
|
-
raise ConverterBase::ValidationError,
|
|
229
|
-
"Invalid hex color for #{field_name}: #{color}. " \
|
|
230
|
-
'Expected format: #RRGGBB or #RGB'
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
# Validate CSS dimension value
|
|
234
|
-
#
|
|
235
|
-
# @param value [String] CSS value to validate
|
|
236
|
-
# @param field_name [String] Field name for error message
|
|
237
|
-
# @raise [ConverterBase::ValidationError] if invalid
|
|
238
|
-
def validate_css_value(value, field_name)
|
|
239
|
-
return unless value
|
|
240
|
-
|
|
241
|
-
# Allow common CSS units
|
|
242
|
-
return if value.match?(/^\d+(\.\d+)?(px|%|ch|em|rem|vw|vh|ms|s)$/)
|
|
243
|
-
|
|
244
|
-
raise ConverterBase::ValidationError,
|
|
245
|
-
"Invalid CSS value for #{field_name}: #{value}. " \
|
|
246
|
-
'Expected format: number with unit (px, %, ch, em, rem, vw, vh, ms, s)'
|
|
247
|
-
end
|
|
248
43
|
end
|
|
249
44
|
|
|
250
|
-
# Convert document to SPA HTML
|
|
251
|
-
#
|
|
252
|
-
# @return [String] Complete HTML5 document with Vue.js application
|
|
253
45
|
def convert
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
46
|
+
renderer = Renderer.new(template_dirs: @config.template_dirs)
|
|
47
|
+
|
|
48
|
+
renderer.render_html5(
|
|
49
|
+
@document,
|
|
50
|
+
layout: :spa,
|
|
51
|
+
lang: @config.lang,
|
|
52
|
+
toc: @config.toc_sticky,
|
|
53
|
+
toc_levels: @config.toc_levels,
|
|
54
|
+
section_numbers: false,
|
|
55
|
+
dist_dir: @config.dist_dir,
|
|
56
|
+
theme_toggle: @config.theme_toggle,
|
|
57
|
+
reading_progress: @config.reading_progress
|
|
58
|
+
)
|
|
260
59
|
end
|
|
261
60
|
|
|
262
61
|
private
|
|
263
62
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
# @param config [Hash, Configuration] Configuration options
|
|
267
|
-
# @return [Configuration] Configuration object
|
|
268
|
-
def build_config(config)
|
|
269
|
-
case config
|
|
270
|
-
when Configuration
|
|
271
|
-
config.validate!
|
|
272
|
-
config
|
|
273
|
-
when Hash
|
|
274
|
-
Configuration.new(**config)
|
|
275
|
-
else
|
|
276
|
-
Configuration.defaults
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
# Output processor interface: unique identifier
|
|
281
|
-
#
|
|
282
|
-
# @return [Symbol] Processor identifier
|
|
283
|
-
def self.processor_id
|
|
284
|
-
:html_spa
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
# Output processor interface: check if this processor handles the file
|
|
288
|
-
#
|
|
289
|
-
# @param filename [String] Output filename
|
|
290
|
-
# @return [Boolean] true if this processor can handle the file
|
|
291
|
-
def self.processor_match?(filename)
|
|
292
|
-
filename.downcase.end_with?('.html', '.htm')
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
# Output processor interface: execute the conversion
|
|
296
|
-
#
|
|
297
|
-
# @param input [Hash] Input from the converter (contains document)
|
|
298
|
-
# @param options [Hash] Output options
|
|
299
|
-
# @return [Hash] Hash with nil => HTML output
|
|
300
|
-
def self.processor_execute(input, options = {})
|
|
301
|
-
# Handle hash input from converter pipeline
|
|
302
|
-
document = input.is_a?(Hash) ? (input[:document] || input.values.first) : input
|
|
303
|
-
html = convert(document, options)
|
|
304
|
-
# Return in format expected by converter (hash with filename => content)
|
|
305
|
-
{ nil => html }
|
|
63
|
+
def configuration_class
|
|
64
|
+
Configuration
|
|
306
65
|
end
|
|
307
66
|
end
|
|
308
67
|
end
|
data/lib/coradoc/html/static.rb
CHANGED
|
@@ -1,164 +1,45 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'coradoc/html/renderer'
|
|
4
|
+
|
|
3
5
|
module Coradoc
|
|
4
6
|
module Html
|
|
5
7
|
autoload :ConverterBase, "#{__dir__}/converter_base"
|
|
6
8
|
|
|
7
9
|
# Static HTML converter
|
|
8
10
|
#
|
|
9
|
-
# Converts CoreModel documents to static HTML5 output
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# @example Basic usage
|
|
13
|
-
# doc = Coradoc.parse_file('document.adoc')
|
|
14
|
-
# html = Coradoc::Html::Static.convert(doc)
|
|
15
|
-
#
|
|
16
|
-
# @example With configuration
|
|
17
|
-
# config = Coradoc::Html::Static::Configuration.new(
|
|
18
|
-
# css_theme: :professional,
|
|
19
|
-
# include_toc: true,
|
|
20
|
-
# theme_toggle: true
|
|
21
|
-
# )
|
|
22
|
-
# html = Coradoc::Html::Static.convert(doc, config)
|
|
23
|
-
#
|
|
24
|
-
# @example Write to file
|
|
25
|
-
# Coradoc::Html::Static.to_file(doc, 'output.html', config)
|
|
11
|
+
# Converts CoreModel documents to static HTML5 output using
|
|
12
|
+
# the unified Liquid template pipeline.
|
|
26
13
|
class Static < ConverterBase
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
attr_accessor :css_theme
|
|
33
|
-
|
|
34
|
-
# How to deliver assets (:embedded, :external)
|
|
35
|
-
attr_accessor :asset_delivery
|
|
36
|
-
|
|
37
|
-
# Whether to include table of contents
|
|
38
|
-
attr_accessor :include_toc
|
|
39
|
-
|
|
40
|
-
# TOC levels to include (1-5)
|
|
41
|
-
attr_accessor :toc_levels
|
|
42
|
-
|
|
43
|
-
# TOC title text
|
|
44
|
-
attr_accessor :toc_title
|
|
45
|
-
|
|
46
|
-
# TOC placement (:auto, :left, :right, :preamble)
|
|
47
|
-
attr_accessor :toc_placement
|
|
48
|
-
|
|
49
|
-
# Whether to enable theme toggle (dark/light mode)
|
|
50
|
-
attr_accessor :theme_toggle
|
|
51
|
-
|
|
52
|
-
# Whether to preserve comments in output
|
|
53
|
-
attr_accessor :preserve_comments
|
|
54
|
-
|
|
55
|
-
# Whether to apply section numbering
|
|
56
|
-
attr_accessor :section_numbering
|
|
57
|
-
|
|
58
|
-
# Maximum section level for numbering
|
|
59
|
-
attr_accessor :section_numbering_levels
|
|
14
|
+
class Configuration < ConverterBase::ConfigurationBase
|
|
15
|
+
attr_accessor :include_toc, :toc_levels,
|
|
16
|
+
:section_numbering, :section_numbering_levels,
|
|
17
|
+
:lang, :meta_tags, :custom_css, :embedded,
|
|
18
|
+
:template_dirs
|
|
60
19
|
|
|
61
|
-
# Language attribute for HTML
|
|
62
|
-
attr_accessor :lang
|
|
63
|
-
|
|
64
|
-
# Custom meta tags
|
|
65
|
-
attr_accessor :meta_tags
|
|
66
|
-
|
|
67
|
-
# Custom CSS to append
|
|
68
|
-
attr_accessor :custom_css
|
|
69
|
-
|
|
70
|
-
# Whether to embed output (no full HTML document)
|
|
71
|
-
attr_accessor :embedded
|
|
72
|
-
|
|
73
|
-
# Valid CSS themes
|
|
74
|
-
VALID_CSS_THEMES = %i[professional academic tech].freeze
|
|
75
|
-
|
|
76
|
-
# Valid asset delivery methods
|
|
77
|
-
VALID_ASSET_DELIVERIES = %i[embedded external].freeze
|
|
78
|
-
|
|
79
|
-
# Valid TOC placements
|
|
80
|
-
VALID_TOC_PLACEMENTS = %i[auto left right preamble].freeze
|
|
81
|
-
|
|
82
|
-
# Initialize configuration with options
|
|
83
|
-
#
|
|
84
|
-
# @param options [Hash] Configuration options
|
|
85
20
|
def initialize(**options)
|
|
86
|
-
@css_theme = options[:css_theme] || :professional
|
|
87
|
-
@asset_delivery = options[:asset_delivery] || :embedded
|
|
88
21
|
@include_toc = options.fetch(:include_toc, false)
|
|
89
22
|
@toc_levels = options[:toc_levels] || 2
|
|
90
|
-
@toc_title = options[:toc_title] || 'Table of Contents'
|
|
91
|
-
@toc_placement = options[:toc_placement] || :auto
|
|
92
|
-
@theme_toggle = options.fetch(:theme_toggle, true)
|
|
93
|
-
@preserve_comments = options.fetch(:preserve_comments, false)
|
|
94
23
|
@section_numbering = options.fetch(:section_numbering, false)
|
|
95
24
|
@section_numbering_levels = options[:section_numbering_levels] || 3
|
|
96
25
|
@lang = options[:lang] || 'en'
|
|
97
26
|
@meta_tags = options[:meta_tags] || {}
|
|
98
27
|
@custom_css = options[:custom_css]
|
|
99
28
|
@embedded = options.fetch(:embedded, false)
|
|
29
|
+
@template_dirs = options[:template_dirs]
|
|
100
30
|
end
|
|
101
31
|
|
|
102
|
-
# Default configuration
|
|
103
|
-
#
|
|
104
|
-
# @return [Configuration] Default configuration instance
|
|
105
|
-
def self.defaults
|
|
106
|
-
new
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Merge with another configuration or hash
|
|
110
|
-
#
|
|
111
|
-
# @param other [Hash, Configuration] Configuration to merge
|
|
112
|
-
# @return [Configuration] New merged configuration
|
|
113
|
-
def merge(other)
|
|
114
|
-
other_hash = other.is_a?(Configuration) ? other.to_h : other.to_h.transform_keys(&:to_sym)
|
|
115
|
-
self.class.new(**to_h.merge(other_hash))
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Convert to hash
|
|
119
|
-
#
|
|
120
|
-
# @return [Hash] Configuration as hash
|
|
121
32
|
def to_h
|
|
122
33
|
{
|
|
123
|
-
|
|
124
|
-
asset_delivery: @asset_delivery,
|
|
125
|
-
include_toc: @include_toc,
|
|
126
|
-
toc_levels: @toc_levels,
|
|
127
|
-
toc_title: @toc_title,
|
|
128
|
-
toc_placement: @toc_placement,
|
|
129
|
-
theme_toggle: @theme_toggle,
|
|
130
|
-
preserve_comments: @preserve_comments,
|
|
34
|
+
include_toc: @include_toc, toc_levels: @toc_levels,
|
|
131
35
|
section_numbering: @section_numbering,
|
|
132
36
|
section_numbering_levels: @section_numbering_levels,
|
|
133
|
-
lang: @lang,
|
|
134
|
-
|
|
135
|
-
custom_css: @custom_css,
|
|
136
|
-
embedded: @embedded
|
|
37
|
+
lang: @lang, meta_tags: @meta_tags, custom_css: @custom_css,
|
|
38
|
+
embedded: @embedded, template_dirs: @template_dirs
|
|
137
39
|
}
|
|
138
40
|
end
|
|
139
41
|
|
|
140
|
-
# Validate configuration
|
|
141
|
-
#
|
|
142
|
-
# @raise [ConverterBase::ValidationError] if configuration is invalid
|
|
143
42
|
def validate!
|
|
144
|
-
unless VALID_CSS_THEMES.include?(@css_theme.to_sym)
|
|
145
|
-
raise ConverterBase::ValidationError,
|
|
146
|
-
"Invalid CSS theme: #{@css_theme}. " \
|
|
147
|
-
"Valid themes: #{VALID_CSS_THEMES.join(', ')}"
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
unless VALID_ASSET_DELIVERIES.include?(@asset_delivery.to_sym)
|
|
151
|
-
raise ConverterBase::ValidationError,
|
|
152
|
-
"Invalid asset delivery: #{@asset_delivery}. " \
|
|
153
|
-
"Valid options: #{VALID_ASSET_DELIVERIES.join(', ')}"
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
if @include_toc && !VALID_TOC_PLACEMENTS.include?(@toc_placement.to_sym)
|
|
157
|
-
raise ConverterBase::ValidationError,
|
|
158
|
-
"Invalid TOC placement: #{@toc_placement}. " \
|
|
159
|
-
"Valid options: #{VALID_TOC_PLACEMENTS.join(', ')}"
|
|
160
|
-
end
|
|
161
|
-
|
|
162
43
|
unless @toc_levels.is_a?(Integer) && @toc_levels.between?(1, 5)
|
|
163
44
|
raise ConverterBase::ValidationError,
|
|
164
45
|
'TOC levels must be an integer between 1 and 5'
|
|
@@ -170,123 +51,33 @@ module Coradoc
|
|
|
170
51
|
'Section numbering levels must be an integer between 1 and 6'
|
|
171
52
|
end
|
|
172
53
|
end
|
|
173
|
-
|
|
174
|
-
# Check if assets should be embedded
|
|
175
|
-
#
|
|
176
|
-
# @return [Boolean] true if assets should be embedded
|
|
177
|
-
def embed_assets?
|
|
178
|
-
@asset_delivery == :embedded || @embedded
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
# Check if external assets should be linked
|
|
182
|
-
#
|
|
183
|
-
# @return [Boolean] true if assets should be linked
|
|
184
|
-
def link_assets?
|
|
185
|
-
!embed_assets?
|
|
186
|
-
end
|
|
187
54
|
end
|
|
188
55
|
|
|
189
|
-
# Convert document to static HTML
|
|
190
|
-
#
|
|
191
|
-
# @return [String] Complete HTML5 document or fragment (if embedded mode)
|
|
192
56
|
def convert
|
|
193
|
-
|
|
194
|
-
options = build_renderer_options
|
|
195
|
-
|
|
196
|
-
# Use ClassicRenderer to generate HTML
|
|
197
|
-
renderer = Html::Theme::ClassicRenderer.new(@document, options)
|
|
57
|
+
renderer = Renderer.new(template_dirs: @config.template_dirs)
|
|
198
58
|
|
|
199
59
|
if @config.embedded
|
|
200
|
-
renderer.render
|
|
60
|
+
renderer.render(@document)
|
|
201
61
|
else
|
|
202
|
-
renderer.render_html5
|
|
62
|
+
renderer.render_html5(
|
|
63
|
+
@document,
|
|
64
|
+
layout: :static,
|
|
65
|
+
lang: @config.lang,
|
|
66
|
+
author: @config.meta_tags[:author],
|
|
67
|
+
description: @config.meta_tags[:description],
|
|
68
|
+
custom_css: @config.custom_css,
|
|
69
|
+
toc: @config.include_toc,
|
|
70
|
+
section_numbers: @config.section_numbering,
|
|
71
|
+
section_number_levels: @config.section_numbering_levels,
|
|
72
|
+
toc_levels: @config.toc_levels
|
|
73
|
+
)
|
|
203
74
|
end
|
|
204
75
|
end
|
|
205
76
|
|
|
206
77
|
private
|
|
207
78
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
# @return [Hash] Options for the classic renderer
|
|
211
|
-
def build_renderer_options
|
|
212
|
-
# When TOC is enabled with auto placement, default to left sidebar
|
|
213
|
-
effective_toc_placement = if @config.include_toc && @config.toc_placement == :auto
|
|
214
|
-
:left
|
|
215
|
-
else
|
|
216
|
-
@config.toc_placement
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
options = {
|
|
220
|
-
theme: :classic,
|
|
221
|
-
css_theme: @config.css_theme.to_s,
|
|
222
|
-
linkcss: @config.link_assets?,
|
|
223
|
-
copycss: true,
|
|
224
|
-
toc: @config.include_toc,
|
|
225
|
-
toclevels: @config.toc_levels,
|
|
226
|
-
toc_title: @config.toc_title,
|
|
227
|
-
toc_placement: effective_toc_placement,
|
|
228
|
-
theme_toggle: @config.theme_toggle,
|
|
229
|
-
preserve_comments: @config.preserve_comments,
|
|
230
|
-
sectnums: @config.section_numbering,
|
|
231
|
-
sectnumlevels: @config.section_numbering_levels,
|
|
232
|
-
lang: @config.lang,
|
|
233
|
-
meta_tags: @config.meta_tags,
|
|
234
|
-
custom_css: @config.custom_css,
|
|
235
|
-
embedded: @config.embedded
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
# Handle JavaScript based on asset delivery
|
|
239
|
-
options[:linkjs] = if @config.embed_assets?
|
|
240
|
-
false
|
|
241
|
-
else
|
|
242
|
-
true
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
options
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
# Build configuration from options
|
|
249
|
-
#
|
|
250
|
-
# @param config [Hash, Configuration] Configuration options
|
|
251
|
-
# @return [Configuration] Configuration object
|
|
252
|
-
def build_config(config)
|
|
253
|
-
case config
|
|
254
|
-
when Configuration
|
|
255
|
-
config.validate!
|
|
256
|
-
config
|
|
257
|
-
when Hash
|
|
258
|
-
Configuration.new(**config)
|
|
259
|
-
else
|
|
260
|
-
Configuration.defaults
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
# Output processor interface: unique identifier
|
|
265
|
-
#
|
|
266
|
-
# @return [Symbol] Processor identifier
|
|
267
|
-
def self.processor_id
|
|
268
|
-
:html_static
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
# Output processor interface: check if this processor handles the file
|
|
272
|
-
#
|
|
273
|
-
# @param filename [String] Output filename
|
|
274
|
-
# @return [Boolean] true if this processor can handle the file
|
|
275
|
-
def self.processor_match?(filename)
|
|
276
|
-
filename.downcase.end_with?('.html', '.htm')
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
# Output processor interface: execute the conversion
|
|
280
|
-
#
|
|
281
|
-
# @param input [Hash] Input from the converter (contains document)
|
|
282
|
-
# @param options [Hash] Output options
|
|
283
|
-
# @return [Hash] Hash with nil => HTML output
|
|
284
|
-
def self.processor_execute(input, options = {})
|
|
285
|
-
# Handle hash input from converter pipeline
|
|
286
|
-
document = input.is_a?(Hash) ? (input[:document] || input.values.first) : input
|
|
287
|
-
html = convert(document, options)
|
|
288
|
-
# Return in format expected by converter (hash with filename => content)
|
|
289
|
-
{ nil => html }
|
|
79
|
+
def configuration_class
|
|
80
|
+
Configuration
|
|
290
81
|
end
|
|
291
82
|
end
|
|
292
83
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'liquid'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Html
|
|
7
|
+
# Shared template caching logic for Renderer and LayoutRenderer.
|
|
8
|
+
#
|
|
9
|
+
# Both Renderer#find_and_load_template and LayoutRenderer#load_layout
|
|
10
|
+
# implement identical caching patterns (check cache, read file, parse
|
|
11
|
+
# Liquid, store in cache, rescue syntax errors). This module provides
|
|
12
|
+
# a single implementation.
|
|
13
|
+
module TemplateCaching
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def load_template(cache:, cache_key:, path:)
|
|
17
|
+
return cache[cache_key] if cache.key?(cache_key)
|
|
18
|
+
|
|
19
|
+
return nil unless path && File.exist?(path)
|
|
20
|
+
|
|
21
|
+
template_content = File.read(path)
|
|
22
|
+
template = Liquid::Template.parse(template_content)
|
|
23
|
+
cache[cache_key] = template
|
|
24
|
+
template
|
|
25
|
+
rescue Liquid::SyntaxError => e
|
|
26
|
+
warn "Template syntax error in #{path}: #{e.message}"
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|