jekyll-webawesome 0.1.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 +7 -0
- data/CHANGELOG.md +17 -0
- data/LICENSE.txt +9 -0
- data/README.md +397 -0
- data/jekyll-webawesome.gemspec +42 -0
- data/lib/jekyll/webawesome/code_block_transformer.rb +110 -0
- data/lib/jekyll/webawesome/plugin.rb +40 -0
- data/lib/jekyll/webawesome/transformer.rb +23 -0
- data/lib/jekyll/webawesome/transformers/badge_transformer.rb +48 -0
- data/lib/jekyll/webawesome/transformers/base_transformer.rb +65 -0
- data/lib/jekyll/webawesome/transformers/button_transformer.rb +68 -0
- data/lib/jekyll/webawesome/transformers/callout_transformer.rb +72 -0
- data/lib/jekyll/webawesome/transformers/card_transformer.rb +121 -0
- data/lib/jekyll/webawesome/transformers/details_transformer.rb +54 -0
- data/lib/jekyll/webawesome/transformers/icon_transformer.rb +82 -0
- data/lib/jekyll/webawesome/transformers/tabs_transformer.rb +53 -0
- data/lib/jekyll/webawesome/transformers/tag_transformer.rb +44 -0
- data/lib/jekyll/webawesome/transformers.rb +14 -0
- data/lib/jekyll/webawesome/version.rb +7 -0
- data/lib/jekyll/webawesome.rb +47 -0
- data/lib/jekyll-webawesome.rb +3 -0
- data/lib/jekyll_webawesome.rb +3 -0
- metadata +155 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
require 'jekyll'
|
5
|
+
require 'kramdown'
|
6
|
+
|
7
|
+
module Jekyll
|
8
|
+
module WebAwesome
|
9
|
+
# Base class for all Web Awesome component transformers
|
10
|
+
# Each transformer should implement the transform method
|
11
|
+
class BaseTransformer
|
12
|
+
def self.transform(content)
|
13
|
+
raise NotImplementedError, 'Subclasses must implement the transform method'
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
protected
|
18
|
+
|
19
|
+
# Helper method to convert markdown content to HTML
|
20
|
+
def markdown_to_html(content)
|
21
|
+
Kramdown::Document.new(content).to_html
|
22
|
+
end
|
23
|
+
|
24
|
+
# Helper method to apply multiple regex patterns with the same transformation logic
|
25
|
+
# @param content [String] The content to transform
|
26
|
+
# @param patterns [Array<Hash>] Array of pattern hashes with :regex and :block
|
27
|
+
# @return [String] The transformed content
|
28
|
+
def apply_multiple_patterns(content, patterns)
|
29
|
+
patterns.each do |pattern|
|
30
|
+
content = content.gsub(pattern[:regex]) do |match|
|
31
|
+
pattern[:block].call(match, $LAST_MATCH_INFO)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
content
|
35
|
+
end
|
36
|
+
|
37
|
+
# Helper method to create both primary and alternative syntax patterns
|
38
|
+
# @param primary_regex [Regexp] The primary syntax regex
|
39
|
+
# @param alternative_regex [Regexp] The alternative syntax regex
|
40
|
+
# @param transform_proc [Proc] The proc that takes captured groups and returns HTML
|
41
|
+
# @return [Array<Hash>] Array of pattern hashes
|
42
|
+
def dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
|
43
|
+
[
|
44
|
+
{
|
45
|
+
regex: primary_regex,
|
46
|
+
block: proc do |_match, matchdata|
|
47
|
+
# Get all captured groups from the matchdata
|
48
|
+
captures = matchdata.captures
|
49
|
+
transform_proc.call(*captures)
|
50
|
+
end
|
51
|
+
},
|
52
|
+
{
|
53
|
+
regex: alternative_regex,
|
54
|
+
block: proc do |_match, matchdata|
|
55
|
+
# Get all captured groups from the matchdata
|
56
|
+
captures = matchdata.captures
|
57
|
+
transform_proc.call(*captures)
|
58
|
+
end
|
59
|
+
}
|
60
|
+
]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_transformer'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module WebAwesome
|
7
|
+
# Transforms button syntax into wa-button elements
|
8
|
+
# Primary syntax: %%%variant?\ncontent\n%%%
|
9
|
+
# Alternative syntax: :::wa-button variant?\ncontent\n:::
|
10
|
+
# Variants: brand, success, neutral, warning, danger
|
11
|
+
#
|
12
|
+
# Link buttons: %%%brand\n[Text](url)\n%%%
|
13
|
+
# Regular buttons: %%%brand\nText\n%%%
|
14
|
+
class ButtonTransformer < BaseTransformer
|
15
|
+
def self.transform(content)
|
16
|
+
# Define both regex patterns
|
17
|
+
primary_regex = /^%%%(brand|success|neutral|warning|danger)?\n(.*?)\n%%%/m
|
18
|
+
alternative_regex = /^:::wa-button\s*(brand|success|neutral|warning|danger)?\n(.*?)\n:::/m
|
19
|
+
|
20
|
+
# Define shared transformation logic
|
21
|
+
transform_proc = proc do |variant, button_content|
|
22
|
+
button_content = button_content.strip
|
23
|
+
|
24
|
+
build_button_html(button_content, variant)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Apply both patterns
|
28
|
+
patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
|
29
|
+
apply_multiple_patterns(content, patterns)
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
private
|
34
|
+
|
35
|
+
def build_button_html(content, variant)
|
36
|
+
variant_attr = variant ? " variant=\"#{variant}\"" : ''
|
37
|
+
|
38
|
+
# Check if content contains a markdown link
|
39
|
+
link_match = content.match(/^\[([^\]]+)\]\(([^)]+)\)$/)
|
40
|
+
|
41
|
+
if link_match
|
42
|
+
# It's a link button
|
43
|
+
link_text = link_match[1]
|
44
|
+
link_url = link_match[2]
|
45
|
+
|
46
|
+
# Process any markdown in the link text (bold, italic, etc.)
|
47
|
+
button_html = markdown_to_html(link_text).strip
|
48
|
+
button_html = button_html.gsub(%r{^<p>(.*)</p>$}m, '\1')
|
49
|
+
|
50
|
+
# Fix whitespace issues like in badges
|
51
|
+
button_html = button_html.gsub(%r{(</\w+>)\s+}, '\1 ')
|
52
|
+
|
53
|
+
"<wa-button#{variant_attr} href=\"#{link_url}\">#{button_html}</wa-button>"
|
54
|
+
else
|
55
|
+
# It's a regular button
|
56
|
+
button_html = markdown_to_html(content).strip
|
57
|
+
button_html = button_html.gsub(%r{^<p>(.*)</p>$}m, '\1')
|
58
|
+
|
59
|
+
# Fix whitespace issues like in badges
|
60
|
+
button_html = button_html.gsub(%r{(</\w+>)\s+}, '\1 ')
|
61
|
+
|
62
|
+
"<wa-button#{variant_attr}>#{button_html}</wa-button>"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_transformer'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module WebAwesome
|
7
|
+
# Transforms callout syntax into wa-callout elements
|
8
|
+
# Primary syntax: :::variant\ncontent\n:::
|
9
|
+
# Alternative syntax: :::wa-callout variant\ncontent\n:::
|
10
|
+
# Variants: info, success, neutral, warning, danger
|
11
|
+
class CalloutTransformer < BaseTransformer
|
12
|
+
def self.transform(content)
|
13
|
+
# Define both regex patterns
|
14
|
+
primary_regex = /^:::(info|success|neutral|warning|danger)\n(.*?)\n:::/m
|
15
|
+
alternative_regex = /^:::wa-callout\s+(info|success|neutral|warning|danger)\n(.*?)\n:::/m
|
16
|
+
|
17
|
+
# Define shared transformation logic
|
18
|
+
transform_proc = proc do |variant, inner_content|
|
19
|
+
attrs = callout_attributes(variant)
|
20
|
+
|
21
|
+
element_tag = "wa-callout#{attrs[:additional_params]}"
|
22
|
+
html_content = "#{attrs[:inner_prepend]}#{markdown_to_html(inner_content)}"
|
23
|
+
|
24
|
+
"<#{element_tag}>#{html_content}</wa-callout>"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Apply both patterns
|
28
|
+
patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
|
29
|
+
apply_multiple_patterns(content, patterns)
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
private
|
34
|
+
|
35
|
+
def callout_attributes(variant)
|
36
|
+
case variant
|
37
|
+
when 'info'
|
38
|
+
{
|
39
|
+
additional_params: ' variant="brand"',
|
40
|
+
inner_prepend: '<wa-icon slot="icon" name="circle-info" variant="regular"></wa-icon>'
|
41
|
+
}
|
42
|
+
when 'success'
|
43
|
+
{
|
44
|
+
additional_params: ' variant="success"',
|
45
|
+
inner_prepend: '<wa-icon slot="icon" name="circle-check" variant="regular"></wa-icon>'
|
46
|
+
}
|
47
|
+
when 'neutral'
|
48
|
+
{
|
49
|
+
additional_params: ' variant="neutral"',
|
50
|
+
inner_prepend: '<wa-icon slot="icon" name="gear" variant="regular"></wa-icon>'
|
51
|
+
}
|
52
|
+
when 'warning'
|
53
|
+
{
|
54
|
+
additional_params: ' variant="warning"',
|
55
|
+
inner_prepend: '<wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon>'
|
56
|
+
}
|
57
|
+
when 'danger'
|
58
|
+
{
|
59
|
+
additional_params: ' variant="danger"',
|
60
|
+
inner_prepend: '<wa-icon slot="icon" name="circle-exclamation" variant="regular"></wa-icon>'
|
61
|
+
}
|
62
|
+
else
|
63
|
+
{
|
64
|
+
additional_params: '',
|
65
|
+
inner_prepend: ''
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_transformer'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module WebAwesome
|
7
|
+
# Transforms card syntax into wa-card elements
|
8
|
+
# Primary syntax: ===appearance?\ncontent\n===
|
9
|
+
# Alternative syntax: :::wa-card appearance?\ncontent\n:::
|
10
|
+
# Appearances: outlined (default), filled, filled-outlined, plain, accent
|
11
|
+
class CardTransformer < BaseTransformer
|
12
|
+
def self.transform(content)
|
13
|
+
# Define both regex patterns
|
14
|
+
primary_regex = /^===(outlined|filled|filled-outlined|plain|accent)?\n(.*?)\n===/m
|
15
|
+
alternative_regex = /^:::wa-card\s*(outlined|filled|filled-outlined|plain|accent)?\n(.*?)\n:::/m
|
16
|
+
|
17
|
+
# Define shared transformation logic
|
18
|
+
transform_proc = proc do |appearance_param, card_content|
|
19
|
+
card_content = card_content.strip
|
20
|
+
|
21
|
+
appearance = normalize_appearance(appearance_param)
|
22
|
+
card_parts = parse_card_content(card_content)
|
23
|
+
|
24
|
+
build_card_html(card_parts, appearance)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Apply both patterns
|
28
|
+
patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
|
29
|
+
apply_multiple_patterns(content, patterns)
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
private
|
34
|
+
|
35
|
+
def normalize_appearance(appearance_param)
|
36
|
+
case appearance_param
|
37
|
+
when 'filled', 'filled-outlined', 'plain', 'accent'
|
38
|
+
appearance_param
|
39
|
+
else
|
40
|
+
'outlined' # default
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_card_content(content)
|
45
|
+
parts = {
|
46
|
+
media: nil,
|
47
|
+
header: nil,
|
48
|
+
content: content,
|
49
|
+
footer: nil
|
50
|
+
}
|
51
|
+
|
52
|
+
# Extract first image as media
|
53
|
+
if content.match(/^!\[([^\]]*)\]\(([^)]+)\)/)
|
54
|
+
parts[:media] = {
|
55
|
+
alt: ::Regexp.last_match(1),
|
56
|
+
src: ::Regexp.last_match(2)
|
57
|
+
}
|
58
|
+
# Remove the image from content
|
59
|
+
content = content.sub(/^!\[([^\]]*)\]\(([^)]+)\)\n?/, '')
|
60
|
+
end
|
61
|
+
|
62
|
+
# Extract first heading as header
|
63
|
+
if content.match(/^# (.+)$/)
|
64
|
+
parts[:header] = ::Regexp.last_match(1).strip
|
65
|
+
# Remove the heading from content
|
66
|
+
content = content.sub(/^# .+\n?/, '')
|
67
|
+
end
|
68
|
+
|
69
|
+
# Extract trailing buttons/links as footer
|
70
|
+
# Look for links or buttons at the end of the content
|
71
|
+
if content.match(/\n\[([^\]]+)\]\(([^)]+)\)\s*$/)
|
72
|
+
parts[:footer] = {
|
73
|
+
text: ::Regexp.last_match(1),
|
74
|
+
href: ::Regexp.last_match(2)
|
75
|
+
}
|
76
|
+
# Remove the footer link from content
|
77
|
+
content = content.sub(/\n\[([^\]]+)\]\(([^)]+)\)\s*$/, '')
|
78
|
+
end
|
79
|
+
|
80
|
+
# Update the main content after extractions
|
81
|
+
parts[:content] = content.strip
|
82
|
+
parts
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_card_html(parts, appearance)
|
86
|
+
attributes = []
|
87
|
+
attributes << "appearance=\"#{appearance}\"" if appearance != 'outlined'
|
88
|
+
|
89
|
+
# Add SSR attributes if slots are present
|
90
|
+
attributes << 'with-media' if parts[:media]
|
91
|
+
attributes << 'with-header' if parts[:header]
|
92
|
+
attributes << 'with-footer' if parts[:footer]
|
93
|
+
|
94
|
+
attr_string = attributes.empty? ? '' : " #{attributes.join(' ')}"
|
95
|
+
|
96
|
+
html_parts = []
|
97
|
+
|
98
|
+
# Media slot
|
99
|
+
html_parts << "<img slot=\"media\" src=\"#{parts[:media][:src]}\" alt=\"#{parts[:media][:alt]}\">" if parts[:media]
|
100
|
+
|
101
|
+
# Header slot
|
102
|
+
if parts[:header]
|
103
|
+
header_html = markdown_to_html(parts[:header])
|
104
|
+
html_parts << "<div slot=\"header\">#{header_html}</div>"
|
105
|
+
end
|
106
|
+
|
107
|
+
# Main content
|
108
|
+
if parts[:content] && !parts[:content].empty?
|
109
|
+
content_html = markdown_to_html(parts[:content])
|
110
|
+
html_parts << content_html
|
111
|
+
end
|
112
|
+
|
113
|
+
# Footer slot
|
114
|
+
html_parts << "<div slot=\"footer\"><wa-button href=\"#{parts[:footer][:href]}\">#{parts[:footer][:text]}</wa-button></div>" if parts[:footer]
|
115
|
+
|
116
|
+
"<wa-card#{attr_string}>#{html_parts.join}</wa-card>"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_transformer'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module WebAwesome
|
7
|
+
# Transforms summary/details syntax into wa-details elements
|
8
|
+
# Primary syntax: ^^^appearance?\nsummary\n>>>\ndetails\n^^^
|
9
|
+
# Alternative syntax: :::wa-details appearance?\nsummary\n>>>\ndetails\n:::
|
10
|
+
# Appearances: outlined (default), filled, filled-outlined, plain
|
11
|
+
class DetailsTransformer < BaseTransformer
|
12
|
+
def self.transform(content)
|
13
|
+
# Define both regex patterns
|
14
|
+
primary_regex = /^\^\^\^?(outlined|filled|filled-outlined|plain)?\n(.*?)\n^>>>\n(.*?)\n^\^\^\^?/m
|
15
|
+
alternative_regex = /^:::wa-details\s*(outlined|filled|filled-outlined|plain)?\n(.*?)\n^>>>\n(.*?)\n:::/m
|
16
|
+
|
17
|
+
# Define shared transformation logic
|
18
|
+
transform_proc = proc do |appearance_param, summary_content, details_content|
|
19
|
+
summary_content = summary_content.strip
|
20
|
+
details_content = details_content.strip
|
21
|
+
|
22
|
+
appearance_class = normalize_appearance(appearance_param)
|
23
|
+
summary_html = markdown_to_html(summary_content)
|
24
|
+
details_html = markdown_to_html(details_content)
|
25
|
+
|
26
|
+
"<wa-details appearance='#{appearance_class}'>" \
|
27
|
+
"<span slot='summary'>#{summary_html}</span>" \
|
28
|
+
"#{details_html}</wa-details>"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Apply both patterns
|
32
|
+
patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
|
33
|
+
apply_multiple_patterns(content, patterns)
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
private
|
38
|
+
|
39
|
+
def normalize_appearance(appearance_param)
|
40
|
+
case appearance_param
|
41
|
+
when 'filled'
|
42
|
+
'filled'
|
43
|
+
when 'filled-outlined'
|
44
|
+
'filled outlined'
|
45
|
+
when 'plain'
|
46
|
+
'plain'
|
47
|
+
else
|
48
|
+
'outlined'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_transformer'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module WebAwesome
|
7
|
+
# Transforms icon syntax into wa-icon elements
|
8
|
+
# Primary syntax: $$$icon-name
|
9
|
+
# Alternative syntax: :::wa-icon icon-name
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
# $$$settings -> <wa-icon name="settings"></wa-icon>
|
13
|
+
# $$$home -> <wa-icon name="home"></wa-icon>
|
14
|
+
# $$$user-circle -> <wa-icon name="user-circle"></wa-icon>
|
15
|
+
class IconTransformer < BaseTransformer
|
16
|
+
def self.transform(content)
|
17
|
+
# Protect code blocks first
|
18
|
+
protected_content, code_blocks = protect_code_blocks(content)
|
19
|
+
|
20
|
+
# Apply primary syntax transformation
|
21
|
+
# Only block patterns that look like incomplete icon names:
|
22
|
+
# $$$icon name (where 'icon name' could be intended as one identifier)
|
23
|
+
result = protected_content.gsub(/\$\$\$([a-zA-Z0-9\-_]+)(?![a-zA-Z0-9\-_]|\s+name\b)/) do
|
24
|
+
icon_name = ::Regexp.last_match(1)
|
25
|
+
build_icon_html(icon_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Apply alternative syntax transformation
|
29
|
+
result = result.gsub(/:::wa-icon\s+([a-zA-Z0-9\-_]+)\s*\n:::/m) do
|
30
|
+
icon_name = ::Regexp.last_match(1)
|
31
|
+
build_icon_html(icon_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Restore code blocks
|
35
|
+
restore_code_blocks(result, code_blocks)
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
private
|
40
|
+
|
41
|
+
def build_icon_html(icon_name)
|
42
|
+
# Clean and validate icon name
|
43
|
+
clean_name = icon_name.strip
|
44
|
+
|
45
|
+
# Return the wa-icon element
|
46
|
+
"<wa-icon name=\"#{clean_name}\"></wa-icon>"
|
47
|
+
end
|
48
|
+
|
49
|
+
def protect_code_blocks(content)
|
50
|
+
code_blocks = {}
|
51
|
+
counter = 0
|
52
|
+
|
53
|
+
# Protect fenced code blocks
|
54
|
+
protected = content.gsub(/```.*?```/m) do |match|
|
55
|
+
placeholder = "ICON_PROTECTED_CODE_BLOCK_#{counter}"
|
56
|
+
code_blocks[placeholder] = match
|
57
|
+
counter += 1
|
58
|
+
placeholder
|
59
|
+
end
|
60
|
+
|
61
|
+
# Protect inline code
|
62
|
+
protected = protected.gsub(/`[^`]+`/) do |match|
|
63
|
+
placeholder = "ICON_PROTECTED_INLINE_CODE_#{counter}"
|
64
|
+
code_blocks[placeholder] = match
|
65
|
+
counter += 1
|
66
|
+
placeholder
|
67
|
+
end
|
68
|
+
|
69
|
+
[protected, code_blocks]
|
70
|
+
end
|
71
|
+
|
72
|
+
def restore_code_blocks(content, code_blocks)
|
73
|
+
result = content
|
74
|
+
code_blocks.each do |placeholder, original|
|
75
|
+
result = result.gsub(placeholder, original)
|
76
|
+
end
|
77
|
+
result
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_transformer'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module WebAwesome
|
7
|
+
# Transforms tabs syntax into wa-tab-group elements
|
8
|
+
# Primary syntax: ++++++placement?\n+++tab1\ncontent\n+++\n+++tab2\ncontent\n+++\n++++++
|
9
|
+
# Alternative syntax: :::wa-tabs placement?\n+++tab1\ncontent\n+++\n+++tab2\ncontent\n+++\n:::
|
10
|
+
# Placements: top (default), bottom, start, end
|
11
|
+
class TabsTransformer < BaseTransformer
|
12
|
+
def self.transform(content)
|
13
|
+
# Define both regex patterns
|
14
|
+
primary_regex = /^\+{6}(top|bottom|start|end)?\n((\+\+\+ [^\n]+\n.*?\n\+\+\+\n?)+)\+{6}/m
|
15
|
+
alternative_regex = /^:::wa-tabs\s*(top|bottom|start|end)?\n((\+\+\+ [^\n]+\n.*?\n\+\+\+\n?)+):::/m
|
16
|
+
|
17
|
+
# Define shared transformation logic
|
18
|
+
transform_proc = proc do |placement, tabs_block, _third_capture|
|
19
|
+
placement ||= 'top'
|
20
|
+
|
21
|
+
tabs, tab_panels = extract_tabs_and_panels(tabs_block)
|
22
|
+
|
23
|
+
"<wa-tab-group placement=\"#{placement}\">#{tabs.join}#{tab_panels.join}</wa-tab-group>"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Apply both patterns
|
27
|
+
patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
|
28
|
+
apply_multiple_patterns(content, patterns)
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
private
|
33
|
+
|
34
|
+
def extract_tabs_and_panels(tabs_block)
|
35
|
+
# Extract individual tabs
|
36
|
+
tab_contents = tabs_block.scan(/^\+\+\+ ([^\n]+)\n(.*?)\n\+\+\+/m)
|
37
|
+
tabs = []
|
38
|
+
tab_panels = []
|
39
|
+
|
40
|
+
tab_contents.each_with_index do |(title, panel_content), index|
|
41
|
+
tab_id = "tab-#{index + 1}"
|
42
|
+
tabs << "<wa-tab panel=\"#{tab_id}\">#{title.strip}</wa-tab>"
|
43
|
+
|
44
|
+
panel_html = markdown_to_html(panel_content.strip)
|
45
|
+
tab_panels << "<wa-tab-panel name=\"#{tab_id}\">#{panel_html}</wa-tab-panel>"
|
46
|
+
end
|
47
|
+
|
48
|
+
[tabs, tab_panels]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_transformer'
|
4
|
+
|
5
|
+
module Jekyll
|
6
|
+
module WebAwesome
|
7
|
+
# Transforms tag syntax into wa-tag elements
|
8
|
+
# Primary syntax: @@@variant?\ncontent\n@@@
|
9
|
+
# Alternative syntax: :::wa-tag variant?\ncontent\n:::
|
10
|
+
# Variants: brand, success, neutral, warning, danger
|
11
|
+
class TagTransformer < BaseTransformer
|
12
|
+
def self.transform(content)
|
13
|
+
# Define both regex patterns
|
14
|
+
primary_regex = /^@@@(brand|success|neutral|warning|danger)?\n(.*?)\n@@@/m
|
15
|
+
alternative_regex = /^:::wa-tag\s*(brand|success|neutral|warning|danger)?\n(.*?)\n:::/m
|
16
|
+
|
17
|
+
# Define shared transformation logic
|
18
|
+
transform_proc = proc do |variant, tag_content|
|
19
|
+
tag_content = tag_content.strip
|
20
|
+
|
21
|
+
build_tag_html(tag_content, variant)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Apply both patterns
|
25
|
+
patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
|
26
|
+
apply_multiple_patterns(content, patterns)
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
private
|
31
|
+
|
32
|
+
def build_tag_html(content, variant)
|
33
|
+
variant_attr = variant ? " variant=\"#{variant}\"" : ''
|
34
|
+
tag_html = markdown_to_html(content).strip
|
35
|
+
|
36
|
+
# Remove paragraph tags if the content is just text
|
37
|
+
tag_html = tag_html.gsub(%r{^<p>(.*)</p>$}m, '\1')
|
38
|
+
|
39
|
+
"<wa-tag#{variant_attr}>#{tag_html}</wa-tag>"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Index file for all Web Awesome transformers
|
4
|
+
# This file makes it easy to require all transformers at once
|
5
|
+
|
6
|
+
require_relative 'transformers/base_transformer'
|
7
|
+
require_relative 'transformers/badge_transformer'
|
8
|
+
require_relative 'transformers/button_transformer'
|
9
|
+
require_relative 'transformers/callout_transformer'
|
10
|
+
require_relative 'transformers/card_transformer'
|
11
|
+
require_relative 'transformers/details_transformer'
|
12
|
+
require_relative 'transformers/icon_transformer'
|
13
|
+
require_relative 'transformers/tabs_transformer'
|
14
|
+
require_relative 'transformers/tag_transformer'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'webawesome/version'
|
4
|
+
require_relative 'webawesome/transformer'
|
5
|
+
require_relative 'webawesome/code_block_transformer'
|
6
|
+
require_relative 'webawesome/plugin'
|
7
|
+
|
8
|
+
module Jekyll
|
9
|
+
# Main module for Jekyll WebAwesome plugin
|
10
|
+
module WebAwesome
|
11
|
+
class Error < StandardError; end
|
12
|
+
|
13
|
+
# Configuration options
|
14
|
+
class << self
|
15
|
+
attr_accessor :configuration
|
16
|
+
|
17
|
+
def configure
|
18
|
+
self.configuration ||= Configuration.new
|
19
|
+
yield(configuration) if block_given?
|
20
|
+
configuration
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Configuration class for future extensibility
|
25
|
+
class Configuration
|
26
|
+
attr_accessor :debug_mode, :callout_icons, :custom_components
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@debug_mode = false
|
30
|
+
@callout_icons = default_callout_icons
|
31
|
+
@custom_components = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def default_callout_icons
|
37
|
+
{
|
38
|
+
info: 'circle-info',
|
39
|
+
success: 'circle-check',
|
40
|
+
neutral: 'gear',
|
41
|
+
warning: 'triangle-exclamation',
|
42
|
+
danger: 'circle-exclamation'
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|