Module: Coradoc::Html::Config

Defined in:
lib/coradoc/html/config.rb

Constant Summary collapse

DEFAULT_LANG =
'en'
DEFAULT_TITLE =
'Untitled'
DEFAULT_OPTIONS =

Default HTML output options

{
  # Theme system options
  theme: :classic,
  modern: {
    # Appearance
    color_scheme: :glass,
    primary_color: '#6366f1',
    accent_color: '#8b5cf6',

    # Layout
    max_width: '1200px',
    content_width: '65ch',
    sidebar_width: '280px',

    # Features
    theme_toggle: true,
    reading_progress: true,
    back_to_top: true,
    toc_sticky: true,
    copy_code_buttons: true,

    # Animation
    enable_animations: true,
    animation_duration: '300ms',

    # Performance
    lazy_load_images: true
  }.freeze,

  # HTML version
  html_version: :html5,

  # Formatting options
  pretty_print: false,
  indent: '  ',
  line_wrap: 0,

  # Content options
  escape_content: true,
  preserve_whitespace: false,
  convert_line_breaks: true,
  preserve_comments: false,

  # Element options
  use_semantic_elements: true,
  add_css_classes: true,
  add_data_attributes: false,

  # Link options
  external_link_target: nil,
  link_rel: nil,

  # Image options
  image_loading: nil,
  image_decoding: nil,

  # Code block options
  syntax_highlighter: nil,
  syntax_highlighter_opts: {},

  # Table options
  table_border: false,
  table_stripes: false,

  # Attribute options
  preserve_custom_attributes: true,
  attribute_prefix: 'data-',

  # CSS & Styling options
  stylesheet: 'coradoc.css',
  stylesdir: './css',
  linkcss: false,
  copycss: true,
  css_theme: 'professional',
  custom_css: nil,

  # JavaScript options
  javascript: 'coradoc.js',
  jsdir: './js',
  linkjs: false,
  theme_toggle: true,
  toc_interactive: nil,

  # Document metadata options
  author: nil,
  description: nil,
  keywords: nil,
  lang: 'en',
  embedded: false,
  meta_tags: {},

  # Table of contents options
  toc: false,
  toclevels: 2,
  toc_title: 'Table of Contents',
  toc_placement: :auto,

  # Section numbering options
  sectnums: false,
  sectnumlevels: 3,

  # Syntax highlighting options
  source_highlighter: nil,
  highlightjs_theme: 'github',
  pygments_style: 'default',
  rouge_style: 'github'
}.freeze
TAG_MAPPING =

Mapping of Coradoc elements to HTML tags

{
  # Sections
  section: 'section',
  header: 'header',

  # Blocks
  paragraph: 'p',
  example: 'div',
  sidebar: 'aside',
  quote: 'blockquote',
  verse: 'div',
  listing: 'pre',
  literal: 'pre',
  source: 'pre',
  open: 'div',

  # Lists
  ordered_list: 'ol',
  unordered_list: 'ul',
  list_item: 'li',
  description_list: 'dl',
  description_term: 'dt',
  description_detail: 'dd',

  # Tables
  table: 'table',
  table_row: 'tr',
  table_cell: 'td',
  table_header: 'th',

  # Inline
  bold: 'strong',
  italic: 'em',
  monospace: 'code',
  highlight: 'mark',
  superscript: 'sup',
  subscript: 'sub',
  underline: 'u',
  strikethrough: 'del',
  small_caps: 'span',

  # Links
  anchor: 'a',
  cross_reference: 'a',

  # Media
  image: 'img',
  video: 'video',
  audio: 'audio',

  # Other
  break: 'hr',
  line_break: 'br',
  admonition: 'div'
}.freeze

Class Method Summary collapse

Class Method Details

.build_text_element(tag_name, content) ⇒ Object



482
483
484
485
486
487
# File 'lib/coradoc/html/config.rb', line 482

def build_text_element(tag_name, content)
  doc = Nokogiri::HTML::Document.new
  node = Nokogiri::XML::Node.new(tag_name, doc)
  node.content = content
  node.to_html
end

.code_block_attributes(language, options = {}) ⇒ Hash

Get data attributes for code block

Parameters:

  • language (String)

    Programming language

  • options (Hash) (defaults to: {})

    Code block options

Returns:

  • (Hash)

    Data attributes



412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/coradoc/html/config.rb', line 412

def code_block_attributes(language, options = {})
  attrs = {}

  attrs[:class] = "language-#{language}" if language && !language.empty?

  if options[:linenums] || options[:line_numbers]
    attrs[:class] =
      [attrs[:class], 'line-numbers'].compact.join(' ')
  end

  attrs
end

.css_class_for(element_type, role = nil) ⇒ Object

Get CSS class for element type



142
143
144
145
146
# File 'lib/coradoc/html/config.rb', line 142

def css_class_for(element_type, role = nil)
  classes = [element_type.to_s.tr('_', '-')]
  classes << role if role
  classes.join(' ')
end

Build CSS link tag



230
231
232
233
234
235
236
237
# File 'lib/coradoc/html/config.rb', line 230

def css_link_tag(options = {})
  href = stylesheet_path(options)
  doc = Nokogiri::HTML::Document.new
  node = Nokogiri::XML::Node.new('link', doc)
  node['rel'] = 'stylesheet'
  node['href'] = href
  node.to_html
end

.css_style_tag(options = {}) ⇒ Object

Build CSS style tag with embedded content



240
241
242
243
244
245
246
247
248
# File 'lib/coradoc/html/config.rb', line 240

def css_style_tag(options = {})
  css_content = embedded_stylesheet(options)
  custom_css = options[:custom_css]

  content = css_content
  content += "\n\n#{custom_css}" if custom_css && !custom_css.empty?

  build_text_element('style', content)
end

.css_tags(options = {}) ⇒ Object

Build complete CSS tags (link or embedded, plus custom)



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/coradoc/html/config.rb', line 265

def css_tags(options = {})
  tags = []

  if embed_css?(options)
    # Embedded mode: include full stylesheet in style tag
    tags << css_style_tag(options)
  else
    # Linked mode: link to external stylesheet
    tags << css_link_tag(options)
    # Add custom CSS separately if provided
    tags << custom_css_tag(options[:custom_css]) if options[:custom_css]
  end

  tags.join("\n")
end

.custom_css_tag(custom_css) ⇒ Object

Build custom CSS style tag



251
252
253
254
255
# File 'lib/coradoc/html/config.rb', line 251

def custom_css_tag(custom_css)
  return '' unless custom_css && !custom_css.empty?

  build_text_element('style', custom_css)
end

.data_attribute_name(name, prefix: 'data-') ⇒ Object

Get data attribute name



149
150
151
# File 'lib/coradoc/html/config.rb', line 149

def data_attribute_name(name, prefix: 'data-')
  "#{prefix}#{name.to_s.tr('_', '-')}"
end

.default_optionsObject

Get default options



122
123
124
# File 'lib/coradoc/html/config.rb', line 122

def default_options
  DEFAULT_OPTIONS.dup
end

.element_config(element_type, options = {}) ⇒ Object

Build element configuration



154
155
156
157
158
159
160
# File 'lib/coradoc/html/config.rb', line 154

def element_config(element_type, options = {})
  {
    tag: html_tag_for(element_type),
    css_class: css_class_for(element_type, options[:role]),
    attributes: options[:attributes] || {}
  }
end

.embed_css?(options = {}) ⇒ Boolean

Determine whether to embed or link CSS

Returns:

  • (Boolean)


258
259
260
261
262
# File 'lib/coradoc/html/config.rb', line 258

def embed_css?(options = {})
  # Embed if linkcss is false or embedded mode is true
  !options.fetch(:linkcss, DEFAULT_OPTIONS[:linkcss]) ||
    options.fetch(:embedded, DEFAULT_OPTIONS[:embedded])
end

.embed_js?(options = {}) ⇒ Boolean

Determine whether to embed or link JavaScript

Returns:

  • (Boolean)


282
283
284
285
286
287
# File 'lib/coradoc/html/config.rb', line 282

def embed_js?(options = {})
  # Embed if linkjs is false, embedded mode is true, or linkcss is false (to match CSS behavior)
  !options.fetch(:linkjs, DEFAULT_OPTIONS[:linkjs]) ||
    options.fetch(:embedded, DEFAULT_OPTIONS[:embedded]) ||
    !options.fetch(:linkcss, DEFAULT_OPTIONS[:linkcss])
end

.embedded_javascript(options = {}) ⇒ Object

Get embedded JavaScript content



302
303
304
305
306
307
308
309
310
311
# File 'lib/coradoc/html/config.rb', line 302

def embedded_javascript(options = {})
  javascript_name = options[:javascript] || DEFAULT_OPTIONS[:javascript]
  asset_path = File.join(__dir__, 'assets', 'js', javascript_name)

  if File.exist?(asset_path)
    File.read(asset_path)
  else
    ''
  end
end

.embedded_stylesheet(options = {}) ⇒ Object

Get embedded stylesheet content



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/coradoc/html/config.rb', line 182

def embedded_stylesheet(options = {})
  css_theme = options[:css_theme] || DEFAULT_OPTIONS[:css_theme]
  stylesheet_name = "#{css_theme}.css"

  # Try themes directory first
  themes_path = File.join(__dir__, 'assets', 'themes', stylesheet_name)
  asset_path = if File.exist?(themes_path)
                 themes_path
               else
                 # Fall back to assets directory for backward compatibility
                 File.join(__dir__, 'assets', stylesheet_name)
               end

  css_content = if File.exist?(asset_path)
                  File.read(asset_path)
                else
                  # Fallback to default coradoc.css
                  default_path = File.join(__dir__, 'assets', 'coradoc.css')
                  File.exist?(default_path) ? File.read(default_path) : ''
                end

  # Resolve @import statements for embedded CSS
  # @import doesn't work in inline <style> tags
  resolve_css_imports(css_content, File.dirname(asset_path))
end

.highlightjs_tags(options = {}) ⇒ String

Build Highlight.js tags

Parameters:

  • options (Hash) (defaults to: {})

    Configuration options

Returns:

  • (String)

    HTML tags for Highlight.js



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
# File 'lib/coradoc/html/config.rb', line 391

def highlightjs_tags(options = {})
  theme = options[:highlightjs_theme] || DEFAULT_OPTIONS[:highlightjs_theme]
  doc = Nokogiri::HTML::Document.new

  link_node = Nokogiri::XML::Node.new('link', doc)
  link_node['rel'] = 'stylesheet'
  link_node['href'] = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/#{theme}.min.css"

  script_node = Nokogiri::XML::Node.new('script', doc)
  script_node['src'] = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'

  init_node = Nokogiri::XML::Node.new('script', doc)
  init_node.content = 'hljs.highlightAll();'

  [link_node.to_html, script_node.to_html, init_node.to_html].join("\n")
end

.html_tag_for(element_type) ⇒ Object

Map element type to HTML tag



163
164
165
# File 'lib/coradoc/html/config.rb', line 163

def html_tag_for(element_type)
  TAG_MAPPING[element_type] || 'div'
end

.javascript_path(options = {}) ⇒ Object

Get JavaScript file path



290
291
292
293
294
295
296
297
298
299
# File 'lib/coradoc/html/config.rb', line 290

def javascript_path(options = {})
  javascript = options[:javascript] || DEFAULT_OPTIONS[:javascript]
  jsdir = options[:jsdir] || DEFAULT_OPTIONS[:jsdir]

  if jsdir && jsdir != '.'
    File.join(jsdir, javascript)
  else
    javascript
  end
end

Build JavaScript link tag



314
315
316
317
318
319
320
321
# File 'lib/coradoc/html/config.rb', line 314

def js_link_tag(options = {})
  src = javascript_path(options)
  doc = Nokogiri::HTML::Document.new
  node = Nokogiri::XML::Node.new('script', doc)
  node['src'] = src
  node['defer'] = ''
  node.to_html
end

.js_script_tag(options = {}) ⇒ Object

Build JavaScript script tag with embedded content



324
325
326
327
328
329
# File 'lib/coradoc/html/config.rb', line 324

def js_script_tag(options = {})
  js_content = embedded_javascript(options)
  return '' if js_content.empty?

  build_text_element('script', js_content)
end

.js_tags(options = {}) ⇒ Object

Build complete JavaScript tags (link or embedded)



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/coradoc/html/config.rb', line 332

def js_tags(options = {})
  return '' if options[:javascript] == false

  tags = []

  tags << if embed_js?(options)
            # Embedded mode: include full JavaScript in script tag
            js_script_tag(options)
          else
            # Linked mode: link to external JavaScript file
            js_link_tag(options)
          end

  tags.join("\n")
end

.merge_options(user_options = {}) ⇒ Object

Merge user options with defaults



127
128
129
# File 'lib/coradoc/html/config.rb', line 127

def merge_options(user_options = {})
  default_options.merge(user_options)
end

.resolve_css_imports(css_content, base_dir) ⇒ String

Resolve @import statements in CSS content

Parameters:

  • css_content (String)

    CSS content with potential @import statements

  • base_dir (String)

    Base directory for resolving relative imports

Returns:

  • (String)

    CSS content with imports resolved



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/coradoc/html/config.rb', line 212

def resolve_css_imports(css_content, base_dir)
  # Match @import url('...') or @import url("...") or @import '...' or @import "..."
  css_content.gsub(/@import\s+(?:url\()?['"]([^'"]+)['"]\)?;?/) do
    import_path = ::Regexp.last_match(1)
    full_path = File.join(base_dir, import_path)

    if File.exist?(full_path)
      # Read the imported file and recursively resolve its imports
      imported_content = File.read(full_path)
      resolve_css_imports(imported_content, File.dirname(full_path))
    else
      # Keep the original import if file not found
      ::Regexp.last_match(0)
    end
  end
end

.stylesheet_path(options = {}) ⇒ Object

Get stylesheet path



168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/coradoc/html/config.rb', line 168

def stylesheet_path(options = {})
  # When linking, use css_theme-based filename, not the stylesheet option
  css_theme = options[:css_theme] || DEFAULT_OPTIONS[:css_theme]
  stylesheet = "#{css_theme}.css"
  stylesdir = options[:stylesdir] || DEFAULT_OPTIONS[:stylesdir]

  if stylesdir && stylesdir != '.'
    File.join(stylesdir, stylesheet)
  else
    stylesheet
  end
end

.syntax_highlighter_tags(options = {}) ⇒ String

Build syntax highlighter tags (CSS and JS)

Parameters:

  • options (Hash) (defaults to: {})

    Configuration options

Returns:

  • (String)

    HTML tags for syntax highlighting



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/coradoc/html/config.rb', line 370

def syntax_highlighter_tags(options = {})
  highlighter = options[:source_highlighter]
  return '' unless highlighter

  case highlighter.to_sym
  when :highlightjs, :highlight_js, :'highlight.js'
    highlightjs_tags(options)
  when :pygments
    # Pygments requires server-side processing, not implemented for client-side HTML
    ''
  when :rouge
    # Rouge requires server-side processing, not implemented for client-side HTML
    ''
  else
    ''
  end
end

.theme_toggle?(options = {}) ⇒ Boolean

Check if theme toggle should be enabled

Returns:

  • (Boolean)


349
350
351
# File 'lib/coradoc/html/config.rb', line 349

def theme_toggle?(options = {})
  options.fetch(:theme_toggle, DEFAULT_OPTIONS[:theme_toggle])
end

.toc_interactive?(options = {}) ⇒ Boolean

Check if interactive TOC should be enabled

Returns:

  • (Boolean)


354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/coradoc/html/config.rb', line 354

def toc_interactive?(options = {})
  # Default to true if TOC is enabled and toc_interactive is not explicitly set to false
  toc_enabled = options.fetch(:toc, DEFAULT_OPTIONS[:toc])
  toc_interactive = options[:toc_interactive]

  # If toc_interactive is nil, default to true when TOC is enabled
  if toc_interactive.nil?
    toc_enabled
  else
    toc_interactive
  end
end

.validate_options(options) ⇒ Object

Validate options

Raises:

  • (ArgumentError)


132
133
134
135
136
137
138
139
# File 'lib/coradoc/html/config.rb', line 132

def validate_options(options)
  valid_keys = DEFAULT_OPTIONS.keys
  invalid_keys = options.keys - valid_keys

  raise ArgumentError, "Invalid options: #{invalid_keys.join(', ')}" unless invalid_keys.empty?

  options
end