markawesome 0.9.4 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ce87485ab7f37e8a96dfabf4d09b95d0fe796cff0ca885eb4aa83a4f013131c
4
- data.tar.gz: cbe429ce4efdc6227b7f390468496f776d4971d09fb1df4ffc47e1aad22a42f1
3
+ metadata.gz: 38226847e3635366ea2442b7e2e4702950c72c4511627e865a269bfef5cfe8b5
4
+ data.tar.gz: 59e5e536facf5d39798a70f7cfa04c951c00098309b88f13ab8733d7e225e843
5
5
  SHA512:
6
- metadata.gz: ad056dfdf6b8cf4c4ded5634994bfb131dd77979c1c23fc6e9ae19f38ed344a4488a7cee14f1cfd30d54674545c02390ec3fa9676ffb1f5e9d2ebccfc9fb3f0d
7
- data.tar.gz: fe3945efdacd009afab96a4a3ba215076c7efd9f5c27f6dea59119b1258b0b85e5e48c17703ef06b3f95b5e6b9b74f345acd331d55c1dcd1c79f75bd27caee12
6
+ metadata.gz: 96879a70ddcaf0869421dabb3b47e2389d26608014ba52f79b58147b3471c2ab08e91bb3b3397d94db785546d20be8f87cf59ab7f4f6bcb68b9a89e59000e088
7
+ data.tar.gz: 45e4e956d65bd8221fba67f3e26a9a72349a32e608c6ebed4c348cc73e5f7edfaeeadacb399d51714db8385b34f16b23f2c966c07f8e0c5339454d6475bff295
data/CHANGELOG.md CHANGED
@@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.10.0] - 2026-05-05
8
+
9
+ ### Added
10
+
11
+ - `Markawesome::PlainMarkdownRenderer` — alternate output format that degrades each Web Awesome component to its closest plain-markdown equivalent: GFM alerts for callouts, `<details>` for details, sequential `###` sections for tabs, plain links for buttons, bold for badges/tags, and so on. Useful for sites that want to publish a clean-markdown copy of their pages alongside the HTML — for example, per-page `.md` endpoints or an `llms.txt`-style index that tools and LLM consumers can read without parsing `<wa-*>` tags.
12
+ - `Markawesome::CodeBlockProtector` — stateless helper that replaces fenced code blocks with opaque placeholders before transformation and restores them after. Extracted from `jekyll-webawesome`'s hook logic so the same guarantee applies anywhere Markawesome is used (Hugo, Middleman, plain Ruby).
13
+ - `render_as_markdown(content, options = {})` class method on every transformer, sitting alongside the existing `transform` method. `Markawesome::Transformer.process` still emits HTML; `Markawesome::PlainMarkdownRenderer.process` emits clean markdown.
14
+ - `Markawesome::PlainMarkdownRenderer.register_override(:component) { |content, opts| ... }` for swapping in a custom rendering of a single component without forking the gem.
15
+
16
+ ### Changed
17
+
18
+ - `Markawesome::Transformer.process` now wraps the pipeline with `CodeBlockProtector.protect/restore`, so fenced code examples that contain `:::`/`^^^`/`@@@` no longer trigger accidental transformation. Previously this protection lived in `jekyll-webawesome`, leaving consumers of Markawesome on other frameworks without it.
19
+
20
+ ## [0.9.5] - 2026-03-18
21
+
22
+ ### Fixed
23
+
24
+ - Added `type='button'` attribute to link-style popover trigger `<button>` elements to satisfy `no-implicit-button-type` HTML validation rule
25
+
7
26
  ## [0.9.4] - 2026-03-13
8
27
 
9
28
  ### Changed
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Markawesome
4
+ # Replaces fenced code blocks with opaque placeholders so that Markawesome's
5
+ # component regexes (`:::`, `^^^`, `@@@`, etc.) cannot match syntax that
6
+ # appears inside example code. Callers are responsible for restoring the
7
+ # blocks after their transformations run.
8
+ #
9
+ # Usage:
10
+ # content, tokens = Markawesome::CodeBlockProtector.protect(content)
11
+ # content = some_transformer.transform(content)
12
+ # content = Markawesome::CodeBlockProtector.restore(content, tokens)
13
+ #
14
+ # The helper is stateless: each call allocates its own token map, so it is
15
+ # safe to use concurrently or nested. The token format is a stable HTML
16
+ # comment so that it survives markdown conversion intact.
17
+ module CodeBlockProtector
18
+ CODE_BLOCK_PATTERN = /```([a-zA-Z0-9.+#_-]+)?(\n.*?)```/m
19
+ PLACEHOLDER_PREFIX = '<!--MARKAWESOME_PROTECTED_CODE_BLOCK_'
20
+ PLACEHOLDER_SUFFIX = '-->'
21
+
22
+ module_function
23
+
24
+ # Replace every fenced code block with a placeholder.
25
+ # @param content [String]
26
+ # @return [Array(String, Hash)] Protected content and placeholder→block map.
27
+ def protect(content)
28
+ tokens = {}
29
+ counter = 0
30
+
31
+ protected_content = content.gsub(CODE_BLOCK_PATTERN) do |match|
32
+ placeholder = "#{PLACEHOLDER_PREFIX}#{counter}#{PLACEHOLDER_SUFFIX}"
33
+ tokens[placeholder] = match
34
+ counter += 1
35
+ placeholder
36
+ end
37
+
38
+ [protected_content, tokens]
39
+ end
40
+
41
+ # Restore placeholders created by {protect}.
42
+ # @param content [String]
43
+ # @param tokens [Hash]
44
+ # @return [String]
45
+ def restore(content, tokens)
46
+ return content if tokens.nil? || tokens.empty?
47
+
48
+ tokens.each do |placeholder, original|
49
+ content = content.gsub(placeholder, original)
50
+ end
51
+ content
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'transformers'
4
+ require_relative 'code_block_protector'
5
+
6
+ module Markawesome
7
+ # Renders Markawesome-flavored markdown into "clean" plain markdown by
8
+ # degrading each Web Awesome component to its closest GFM equivalent.
9
+ # Used to serve per-page `.md` endpoints and to generate llms.txt content
10
+ # that LLM consumers can read without having to understand `<wa-*>` tags.
11
+ #
12
+ # Mirrors Markawesome::Transformer.process, but dispatches to each
13
+ # transformer's `render_as_markdown` method instead of `transform`.
14
+ class PlainMarkdownRenderer
15
+ # Per-component overrides registered by host applications. Values are
16
+ # procs with signature `proc { |content, options| ... }` that replace
17
+ # the default rendering for that component.
18
+ @overrides = {}
19
+
20
+ class << self
21
+ attr_reader :overrides
22
+
23
+ # Register a per-component override. Consumers can call this from a
24
+ # plugin during boot to override the default rendering for a single
25
+ # component without forking the gem.
26
+ #
27
+ # @param component [Symbol] one of :callout, :badge, :button, :card,
28
+ # :carousel, :comparison, :copy_button, :details, :dialog, :icon,
29
+ # :image_dialog, :layout, :popover, :tabs, :tag.
30
+ # @yield [content, options] Proc that receives the full source content
31
+ # and the renderer options; returns the content with that component
32
+ # syntax replaced.
33
+ def register_override(component, &block)
34
+ raise ArgumentError, 'block required' unless block_given?
35
+
36
+ @overrides[component.to_sym] = block
37
+ end
38
+
39
+ # Clear all registered overrides (useful in tests).
40
+ def reset_overrides!
41
+ @overrides = {}
42
+ end
43
+ end
44
+
45
+ PIPELINE = %i[
46
+ layout
47
+ popover
48
+ badge
49
+ button
50
+ callout
51
+ card
52
+ carousel
53
+ comparison
54
+ copy_button
55
+ details
56
+ image_dialog
57
+ dialog
58
+ icon
59
+ tag
60
+ tabs
61
+ ].freeze
62
+
63
+ TRANSFORMER_MAP = {
64
+ layout: LayoutTransformer,
65
+ popover: PopoverTransformer,
66
+ badge: BadgeTransformer,
67
+ button: ButtonTransformer,
68
+ callout: CalloutTransformer,
69
+ card: CardTransformer,
70
+ carousel: CarouselTransformer,
71
+ comparison: ComparisonTransformer,
72
+ copy_button: CopyButtonTransformer,
73
+ details: DetailsTransformer,
74
+ image_dialog: ImageDialogTransformer,
75
+ dialog: DialogTransformer,
76
+ icon: IconTransformer,
77
+ tag: TagTransformer,
78
+ tabs: TabsTransformer
79
+ }.freeze
80
+
81
+ def self.process(content, options = {})
82
+ content, tokens = CodeBlockProtector.protect(content)
83
+
84
+ PIPELINE.each do |component|
85
+ if (override = overrides[component])
86
+ content = override.call(content, options)
87
+ next
88
+ end
89
+
90
+ transformer = TRANSFORMER_MAP.fetch(component)
91
+ content = if component == :image_dialog
92
+ if options[:image_dialog]
93
+ config = options[:image_dialog].is_a?(Hash) ? options[:image_dialog] : {}
94
+ transformer.render_as_markdown(content, config)
95
+ else
96
+ content
97
+ end
98
+ else
99
+ transformer.render_as_markdown(content, options)
100
+ end
101
+ end
102
+
103
+ content = strip_kramdown_attributes(content)
104
+ CodeBlockProtector.restore(content, tokens)
105
+ end
106
+
107
+ # Strip Kramdown attribute syntax like `{:.class}`, `{:#id}`, `{: .class}`,
108
+ # `{: #id .class}`, etc. These are Kramdown-specific and not valid GFM.
109
+ def self.strip_kramdown_attributes(content)
110
+ content.gsub(/\s*\{:\s*[^}]*\}/, '')
111
+ end
112
+ end
113
+ end
@@ -2,11 +2,14 @@
2
2
 
3
3
  require 'kramdown'
4
4
  require_relative 'transformers'
5
+ require_relative 'code_block_protector'
5
6
 
6
7
  module Markawesome
7
8
  # Main transformer that orchestrates all component transformers
8
9
  class Transformer
9
10
  def self.process(content, options = {})
11
+ content, tokens = CodeBlockProtector.protect(content)
12
+
10
13
  content = LayoutTransformer.transform(content)
11
14
  content = PopoverTransformer.transform(content)
12
15
  content = BadgeTransformer.transform(content)
@@ -27,7 +30,9 @@ module Markawesome
27
30
  content = DialogTransformer.transform(content)
28
31
  content = IconTransformer.transform(content)
29
32
  content = TagTransformer.transform(content)
30
- TabsTransformer.transform(content)
33
+ content = TabsTransformer.transform(content)
34
+
35
+ CodeBlockProtector.restore(content, tokens)
31
36
  end
32
37
  end
33
38
  end
@@ -41,6 +41,19 @@ module Markawesome
41
41
  apply_multiple_patterns(content, patterns)
42
42
  end
43
43
 
44
+ def self.render_as_markdown(content, _options = {})
45
+ primary_regex = /^!!!(.*?)\n(.*?)\n!!!/m
46
+ alternative_regex = /^:::wa-badge\s*(.*?)\n(.*?)\n:::/m
47
+
48
+ transform_proc = proc do |_params_string, badge_content|
49
+ text = badge_content.to_s.strip
50
+ text.empty? ? '' : "**#{text}**"
51
+ end
52
+
53
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
54
+ apply_multiple_patterns(content, patterns)
55
+ end
56
+
44
57
  class << self
45
58
  private
46
59
 
@@ -55,6 +55,23 @@ module Markawesome
55
55
  apply_multiple_patterns(content, patterns)
56
56
  end
57
57
 
58
+ def self.render_as_markdown(content, _options = {})
59
+ primary_regex = /^%%%([^\n]*)\n(.*?)\n%%%/m
60
+ alternative_regex = /^:::wa-button\s*([^\n]*)\n(.*?)\n:::/m
61
+
62
+ transform_proc = proc do |_params_string, button_content|
63
+ text = button_content.to_s.strip
64
+ if (link_match = text.match(/^\[([^\]]+)\]\(([^)]+)\)$/))
65
+ "[#{link_match[1]}](#{link_match[2]})"
66
+ else
67
+ text.empty? ? '' : "**#{text}**"
68
+ end
69
+ end
70
+
71
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
72
+ apply_multiple_patterns(content, patterns)
73
+ end
74
+
58
75
  class << self
59
76
  private
60
77
 
@@ -51,6 +51,31 @@ module Markawesome
51
51
  apply_multiple_patterns(content, patterns)
52
52
  end
53
53
 
54
+ GFM_ALERT_MAP = {
55
+ 'info' => 'NOTE',
56
+ 'brand' => 'NOTE',
57
+ 'success' => 'TIP',
58
+ 'neutral' => 'IMPORTANT',
59
+ 'warning' => 'WARNING',
60
+ 'danger' => 'CAUTION'
61
+ }.freeze
62
+
63
+ def self.render_as_markdown(content, _options = {})
64
+ variant_pattern = VARIANTS.join('|')
65
+ primary_regex = /^:::(#{variant_pattern})([^\n]*)\n(.*?)\n:::/m
66
+ alternative_regex = /^:::wa-callout\s+(#{variant_pattern})([^\n]*)\n(.*?)\n:::/m
67
+
68
+ transform_proc = proc do |variant, _extra_params, inner_content|
69
+ alert = GFM_ALERT_MAP.fetch(variant, 'NOTE')
70
+ body = inner_content.to_s.strip
71
+ quoted = body.empty? ? '' : body.split("\n").map { |l| l.empty? ? '>' : "> #{l}" }.join("\n")
72
+ body.empty? ? "> [!#{alert}]" : "> [!#{alert}]\n#{quoted}"
73
+ end
74
+
75
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
76
+ apply_multiple_patterns(content, patterns)
77
+ end
78
+
54
79
  class << self
55
80
  private
56
81
 
@@ -37,9 +37,35 @@ module Markawesome
37
37
  apply_multiple_patterns(content, patterns)
38
38
  end
39
39
 
40
+ def self.render_as_markdown(content, _options = {})
41
+ primary_regex = /^===([^\n]*)\n(.*?)\n===/m
42
+ alternative_regex = /^:::wa-card\s*([^\n]*)\n(.*?)\n:::/m
43
+
44
+ transform_proc = proc do |_params_string, card_content|
45
+ parts = parse_card_content(card_content.to_s.strip)
46
+ render_card_markdown(parts)
47
+ end
48
+
49
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
50
+ apply_multiple_patterns(content, patterns)
51
+ end
52
+
40
53
  class << self
41
54
  private
42
55
 
56
+ def render_card_markdown(parts)
57
+ blocks = []
58
+ if parts[:media]
59
+ blocks << "![#{parts[:media][:alt]}](#{parts[:media][:src]})"
60
+ end
61
+ blocks << "### #{parts[:header]}" if parts[:header]
62
+ blocks << parts[:content] if parts[:content] && !parts[:content].empty?
63
+ if parts[:footer]
64
+ blocks << "[#{parts[:footer][:text]}](#{parts[:footer][:href]})"
65
+ end
66
+ blocks.join("\n\n")
67
+ end
68
+
43
69
  def parse_card_content(content)
44
70
  parts = {
45
71
  media: nil,
@@ -44,6 +44,19 @@ module Markawesome
44
44
  apply_multiple_patterns(content, patterns)
45
45
  end
46
46
 
47
+ def self.render_as_markdown(content, _options = {})
48
+ primary_regex = /^~{6}([^\n]*)\n((?:~~~\n(?:.*?\n)?~~~\n?)+)~{6}/m
49
+ alternative_regex = /^:::wa-carousel\s*([^\n]*)\n((?:~~~\n(?:.*?\n)?~~~\n?)+):::/m
50
+
51
+ transform_proc = proc do |_params, slides_block, _third|
52
+ slides = extract_slides(slides_block)
53
+ slides.reject(&:empty?).join("\n\n")
54
+ end
55
+
56
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
57
+ apply_multiple_patterns(content, patterns)
58
+ end
59
+
47
60
  class << self
48
61
  private
49
62
 
@@ -38,9 +38,27 @@ module Markawesome
38
38
  end
39
39
  end
40
40
 
41
+ def self.render_as_markdown(content, _options = {})
42
+ content = content.gsub(/^\|\|\|(\d+)?\n(.*?)\n\|\|\|/m) do |match|
43
+ images = extract_images(::Regexp.last_match(2))
44
+ images.length == 2 ? render_comparison_markdown(images) : match
45
+ end
46
+
47
+ content.gsub(/^:::wa-comparison\s*(\d+)?\n(.*?)\n:::/m) do |match|
48
+ images = extract_images(::Regexp.last_match(2))
49
+ images.length == 2 ? render_comparison_markdown(images) : match
50
+ end
51
+ end
52
+
41
53
  class << self
42
54
  private
43
55
 
56
+ def render_comparison_markdown(images)
57
+ before_alt, before_src = images[0]
58
+ after_alt, after_src = images[1]
59
+ "**Before:** ![#{before_alt}](#{before_src})\n\n**After:** ![#{after_alt}](#{after_src})"
60
+ end
61
+
44
62
  def build_comparison_html(content, position = nil)
45
63
  images = extract_images(content)
46
64
 
@@ -66,6 +66,18 @@ module Markawesome
66
66
  apply_multiple_patterns(content, patterns)
67
67
  end
68
68
 
69
+ def self.render_as_markdown(content, _options = {})
70
+ primary_regex = /^<<<(.*?)\n(.*?)\n<<</m
71
+ alternative_regex = /^:::wa-copy-button\s*(.*?)\n(.*?)\n:::/m
72
+
73
+ transform_proc = proc do |_params_string, copy_content|
74
+ copy_content.to_s.strip
75
+ end
76
+
77
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
78
+ apply_multiple_patterns(content, patterns)
79
+ end
80
+
69
81
  class << self
70
82
  private
71
83
 
@@ -66,6 +66,20 @@ module Markawesome
66
66
  apply_multiple_patterns(content, patterns)
67
67
  end
68
68
 
69
+ def self.render_as_markdown(content, _options = {})
70
+ primary_regex = /^\^\^\^?(.*?)\n(.*?)\n^>>>\n(.*?)\n^\^\^\^?/m
71
+ alternative_regex = /^:::wa-details\s*(.*?)\n(.*?)\n^>>>\n(.*?)\n:::/m
72
+
73
+ transform_proc = proc do |_params_string, summary_content, details_content|
74
+ summary = summary_content.to_s.strip
75
+ details = details_content.to_s.strip
76
+ "<details>\n<summary>#{summary}</summary>\n\n#{details}\n</details>"
77
+ end
78
+
79
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
80
+ apply_multiple_patterns(content, patterns)
81
+ end
82
+
69
83
  class << self
70
84
  private
71
85
 
@@ -52,6 +52,25 @@ module Markawesome
52
52
  apply_multiple_patterns(content, patterns)
53
53
  end
54
54
 
55
+ def self.render_as_markdown(content, _options = {})
56
+ primary_regex = /^\?\?\?([^\n]*)$\n(.*?)\n^>>>$\n(.*?)\n^\?\?\?$/m
57
+ alternative_regex = /^:::wa-dialog([^\n]*)$\n(.*?)\n^>>>$\n(.*?)\n^:::$/m
58
+
59
+ transform_proc = proc do |_params_string, button_text, dialog_content|
60
+ trigger = button_text.to_s.strip
61
+ body = dialog_content.to_s.strip
62
+ # If dialog body already starts with a heading, use it verbatim.
63
+ if body.match?(/^#\s+/)
64
+ "_#{trigger}:_\n\n#{body}"
65
+ else
66
+ "_#{trigger}:_\n\n#{body}"
67
+ end
68
+ end
69
+
70
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
71
+ apply_multiple_patterns(content, patterns)
72
+ end
73
+
55
74
  class << self
56
75
  private
57
76
 
@@ -34,6 +34,16 @@ module Markawesome
34
34
  restore_code_blocks(result, code_blocks)
35
35
  end
36
36
 
37
+ def self.render_as_markdown(content, _options = {})
38
+ protected_content, code_blocks = protect_code_blocks(content)
39
+
40
+ # Drop primary-syntax icons entirely.
41
+ result = protected_content.gsub(/\$\$\$([a-zA-Z0-9\-_]+)(?![a-zA-Z0-9\-_]|\s+name\b)/, '')
42
+ result = result.gsub(/:::wa-icon\s+([a-zA-Z0-9\-_]+)\s*\n:::/m, '')
43
+
44
+ restore_code_blocks(result, code_blocks)
45
+ end
46
+
37
47
  class << self
38
48
  private
39
49
 
@@ -40,6 +40,12 @@ module Markawesome
40
40
  restore_fenced_code_blocks(result, fenced_code_blocks)
41
41
  end
42
42
 
43
+ # For plain-markdown output image-dialog wrapping is dropped: the raw
44
+ # markdown image syntax is already what we want.
45
+ def self.render_as_markdown(content, _config = {})
46
+ content
47
+ end
48
+
43
49
  class << self
44
50
  private
45
51
 
@@ -48,6 +48,18 @@ module Markawesome
48
48
  apply_multiple_patterns(content, patterns)
49
49
  end
50
50
 
51
+ def self.render_as_markdown(content, _options = {})
52
+ primary_regex = /^::::(grid|stack|cluster|split|flank|frame)[ \t]*([^\n]*)\n(.*?)\n::::/m
53
+ alternative_regex = /^::::wa-(grid|stack|cluster|split|flank|frame)[ \t]*([^\n]*)\n(.*?)\n::::/m
54
+
55
+ transform_proc = proc do |_type, _params_string, inner_content|
56
+ inner_content.to_s
57
+ end
58
+
59
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
60
+ apply_multiple_patterns(content, patterns)
61
+ end
62
+
51
63
  class << self
52
64
  private
53
65
 
@@ -71,6 +71,32 @@ module Markawesome
71
71
  apply_multiple_patterns(content, patterns)
72
72
  end
73
73
 
74
+ def self.render_as_markdown(content, _options = {})
75
+ inline_regex = /&&&[ \t]*([^\r\n]*?)[ \t]*>>>[ \t]*([^\r\n]+?)[ \t]*&&&/
76
+ primary_regex = /^&&&([^\n]*)$\n(.*?)\n^>>>$\n(.*?)\n^&&&$/m
77
+ alternative_regex = /^:::wa-popover([^\n]*)$\n(.*?)\n^>>>$\n(.*?)\n^:::$/m
78
+
79
+ inline_transform = {
80
+ regex: inline_regex,
81
+ block: proc do |_match, matchdata|
82
+ combined = matchdata[1]
83
+ popover_content = matchdata[2].strip
84
+ _params, trigger_text = parse_inline_trigger_and_params(combined)
85
+ "**#{trigger_text}** (#{popover_content})"
86
+ end
87
+ }
88
+
89
+ block_transform_proc = proc do |_params_string, trigger_text, popover_content|
90
+ "**#{trigger_text.to_s.strip}**\n\n#{popover_content.to_s.strip}"
91
+ end
92
+
93
+ patterns = [
94
+ inline_transform,
95
+ *dual_syntax_patterns(primary_regex, alternative_regex, block_transform_proc)
96
+ ]
97
+ apply_multiple_patterns(content, patterns)
98
+ end
99
+
74
100
  class << self
75
101
  private
76
102
 
@@ -167,7 +193,7 @@ module Markawesome
167
193
  'color: inherit; text-decoration: underline; ' \
168
194
  'text-decoration-style: dotted; ' \
169
195
  'cursor: pointer; font: inherit;'
170
- "<button id='#{popover_id}' class='ma-popover-trigger' style='#{link_style_attr}'>#{trigger_content}</button>"
196
+ "<button type='button' id='#{popover_id}' class='ma-popover-trigger' style='#{link_style_attr}'>#{trigger_content}</button>"
171
197
  else
172
198
  "<wa-button id='#{popover_id}' variant='text'>#{trigger_content}</wa-button>"
173
199
  end
@@ -59,6 +59,21 @@ module Markawesome
59
59
  apply_multiple_patterns(content, patterns)
60
60
  end
61
61
 
62
+ def self.render_as_markdown(content, _options = {})
63
+ primary_regex = /^\+{6}([^\n]*)\n((\+\+\+ [^\n]+\n.*?\n\+\+\+\n?)+)\+{6}/m
64
+ alternative_regex = /^:::wa-tab-group\s*([^\n]*)\n((\+\+\+ [^\n]+\n.*?\n\+\+\+\n?)+):::/m
65
+
66
+ transform_proc = proc do |_params_string, tabs_block, _third|
67
+ tab_contents = tabs_block.scan(/^\+\+\+ ([^\n]+)\n(.*?)\n\+\+\+/m)
68
+ tab_contents.map do |title, panel_content|
69
+ "### #{title.strip}\n\n#{panel_content.strip}"
70
+ end.join("\n\n")
71
+ end
72
+
73
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
74
+ apply_multiple_patterns(content, patterns)
75
+ end
76
+
62
77
  class << self
63
78
  private
64
79
 
@@ -84,6 +84,37 @@ module Markawesome
84
84
  apply_multiple_patterns(content, patterns)
85
85
  end
86
86
 
87
+ def self.render_as_markdown(content, _options = {})
88
+ primary_regex = /^@@@([^\r\n]*?)\r?\n(.*?)\r?\n@@@/m
89
+ alternative_regex = /^:::wa-tag\s*([^\r\n]*?)\r?\n(.*?)\r?\n:::/m
90
+ inline_regex = /@@@\h*([^@\r\n]+?)\h*@@@/
91
+
92
+ block_transform_proc = proc do |_params, tag_content|
93
+ text = tag_content.to_s.strip
94
+ text.empty? ? '' : "**#{text}**"
95
+ end
96
+
97
+ inline_transform = {
98
+ regex: inline_regex,
99
+ block: proc do |_match, matchdata|
100
+ full_content = matchdata[1].to_s.strip
101
+ # Strip attribute tokens (variant, appearance, size, pill, with-remove, icon:...)
102
+ content_tokens = full_content.split(/\s+/).reject do |token|
103
+ COMPONENT_ATTRIBUTES.any? { |_attr, values| values.include?(token) } ||
104
+ token.start_with?('icon:')
105
+ end
106
+ rendered = content_tokens.any? ? content_tokens.join(' ') : full_content
107
+ rendered.empty? ? '' : "**#{rendered}**"
108
+ end
109
+ }
110
+
111
+ patterns = [
112
+ inline_transform,
113
+ *dual_syntax_patterns(primary_regex, alternative_regex, block_transform_proc)
114
+ ]
115
+ apply_multiple_patterns(content, patterns)
116
+ end
117
+
87
118
  class << self
88
119
  private
89
120
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Markawesome
4
- VERSION = '0.9.4'
4
+ VERSION = '0.10.0'
5
5
  end
data/lib/markawesome.rb CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  require_relative 'markawesome/version'
4
4
  require_relative 'markawesome/icon_slot_parser'
5
+ require_relative 'markawesome/code_block_protector'
5
6
  require_relative 'markawesome/transformer'
7
+ require_relative 'markawesome/plain_markdown_renderer'
6
8
 
7
9
  # Main module for Markawesome - framework-agnostic Markdown to Web Awesome component transformer
8
10
  module Markawesome
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markawesome
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.4
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janne Waren
@@ -92,7 +92,9 @@ files:
92
92
  - README.md
93
93
  - lib/markawesome.rb
94
94
  - lib/markawesome/attribute_parser.rb
95
+ - lib/markawesome/code_block_protector.rb
95
96
  - lib/markawesome/icon_slot_parser.rb
97
+ - lib/markawesome/plain_markdown_renderer.rb
96
98
  - lib/markawesome/transformer.rb
97
99
  - lib/markawesome/transformers.rb
98
100
  - lib/markawesome/transformers/badge_transformer.rb