jekyll-webawesome 0.6.0 → 0.7.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.
@@ -1,82 +0,0 @@
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
@@ -1,174 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'digest'
4
- require_relative 'base_transformer'
5
-
6
- module Jekyll
7
- module WebAwesome
8
- # Transforms standalone images into clickable images that open in dialogs
9
- # Images can opt-out by adding "nodialog" to the title attribute
10
- # Example: ![Alt text](image.png "nodialog")
11
- class ImageDialogTransformer < BaseTransformer
12
- def self.transform(content, site = nil)
13
- # Get configuration including default width
14
- config = site ? Plugin.image_dialog_config(site) : {}
15
-
16
- # First, protect code blocks, inline code, and comparison blocks from transformation
17
- protected_content, fenced_code_blocks = protect_fenced_code_blocks(content)
18
- protected_content, inline_code_blocks = protect_inline_code(protected_content)
19
- protected_content, comparison_blocks = protect_comparisons(protected_content)
20
-
21
- # Match markdown images: ![alt](url) or ![alt](url "title")
22
- # Capture alt text, URL, and optional title
23
- # URL can contain spaces and special characters
24
- image_regex = /!\[([^\]]*)\]\(([^)]+?)(?:\s+"([^"]*)")?\)/
25
-
26
- result = protected_content.gsub(image_regex) do |match|
27
- alt_text = Regexp.last_match(1)
28
- image_url = Regexp.last_match(2).strip
29
- title = Regexp.last_match(3)
30
-
31
- # Skip transformation if title contains "nodialog"
32
- if title&.include?('nodialog')
33
- # Return original image without dialog
34
- match
35
- else
36
- # Transform to clickable image with dialog
37
- transform_to_dialog(alt_text, image_url, title, config)
38
- end
39
- end
40
-
41
- # Restore protected blocks in reverse order
42
- result = restore_comparisons(result, comparison_blocks)
43
- result = restore_inline_code(result, inline_code_blocks)
44
- restore_fenced_code_blocks(result, fenced_code_blocks)
45
- end
46
-
47
- class << self
48
- private
49
-
50
- # Protect fenced code blocks from transformation
51
- def protect_fenced_code_blocks(content)
52
- code_blocks = []
53
- # Match both ``` and ~~~ style code blocks with optional language
54
- protected = content.gsub(/^```.*?^```$|^~~~.*?^~~~$/m) do |match|
55
- placeholder = "<!--IMAGE_DIALOG_FENCED_CODE_#{code_blocks.length}-->"
56
- code_blocks << match
57
- placeholder
58
- end
59
- [protected, code_blocks]
60
- end
61
-
62
- # Restore protected fenced code blocks
63
- def restore_fenced_code_blocks(content, code_blocks)
64
- code_blocks.each_with_index do |code, index|
65
- content = content.gsub("<!--IMAGE_DIALOG_FENCED_CODE_#{index}-->", code)
66
- end
67
- content
68
- end
69
-
70
- # Protect inline code from transformation
71
- def protect_inline_code(content)
72
- code_blocks = []
73
- protected = content.gsub(/`[^`]+`/) do |match|
74
- placeholder = "<!--IMAGE_DIALOG_INLINE_CODE_#{code_blocks.length}-->"
75
- code_blocks << match
76
- placeholder
77
- end
78
- [protected, code_blocks]
79
- end
80
-
81
- # Restore protected inline code
82
- def restore_inline_code(content, code_blocks)
83
- code_blocks.each_with_index do |code, index|
84
- content = content.gsub("<!--IMAGE_DIALOG_INLINE_CODE_#{index}-->", code)
85
- end
86
- content
87
- end
88
-
89
- # Protect comparison blocks from image transformation
90
- # Must protect both markdown syntax (|||...|||) and already-transformed HTML
91
- def protect_comparisons(content)
92
- comparison_blocks = []
93
-
94
- # First protect markdown comparison syntax: |||...|||
95
- protected = content.gsub(/\|\|\|(\d+)?\n.*?\n\|\|\|/m) do |match|
96
- placeholder = "<!--IMAGE_DIALOG_COMPARISON_#{comparison_blocks.length}-->"
97
- comparison_blocks << match
98
- placeholder
99
- end
100
-
101
- # Also protect already-transformed HTML comparison blocks: <wa-comparison ...>...</wa-comparison>
102
- protected = protected.gsub(/<wa-comparison[^>]*>.*?<\/wa-comparison>/m) do |match|
103
- placeholder = "<!--IMAGE_DIALOG_COMPARISON_#{comparison_blocks.length}-->"
104
- comparison_blocks << match
105
- placeholder
106
- end
107
-
108
- [protected, comparison_blocks]
109
- end
110
-
111
- # Restore protected comparison blocks
112
- def restore_comparisons(content, comparison_blocks)
113
- comparison_blocks.each_with_index do |block, index|
114
- content = content.gsub("<!--IMAGE_DIALOG_COMPARISON_#{index}-->", block)
115
- end
116
- content
117
- end
118
-
119
- # Transform image into our custom dialog syntax
120
- # This will be processed by DialogTransformer to create the actual wa-dialog
121
- def transform_to_dialog(alt_text, image_url, title, config = {})
122
- # Parse width from title if specified (e.g., "50%", "800px", "60vw")
123
- width = extract_width_from_title(title)
124
-
125
- # Use default width from config if no width specified in title
126
- width ||= config[:default_width] if config[:default_width]
127
-
128
- # Build dialog parameters
129
- # Always include header with X close button for accessibility
130
- params = ['light-dismiss']
131
- params << width if width
132
- params_string = params.join(' ')
133
-
134
- # Build the button content - a styled image that acts as the trigger
135
- # Add title attribute if provided and doesn't contain "nodialog" or width
136
- title_attr = title && !title.include?('nodialog') && !contains_width?(title) ? " title=\"#{title}\"" : ''
137
- button_content = "<img src=\"#{image_url}\" alt=\"#{alt_text}\" style=\"cursor: zoom-in; display: block; width: 100%; height: auto;\"#{title_attr} />"
138
-
139
- # Build the dialog content with alt text as heading for the label
140
- # Use alt text for the label, or "Image" as fallback if alt is empty
141
- label_text = alt_text.empty? ? 'Image' : alt_text
142
- dialog_content = "# #{label_text}\n\n<img src=\"#{image_url}\" alt=\"#{alt_text}\" style=\"max-width: 100%; height: auto; display: block; margin: 0 auto;\" />"
143
-
144
- # Use our custom dialog syntax that will be processed by DialogTransformer
145
- # Format: ???params\nbutton_content\n>>>\ndialog_content\n???
146
- result = []
147
- result << "???#{params_string}"
148
- result << button_content
149
- result << '>>>'
150
- result << dialog_content
151
- result << '???'
152
-
153
- result.join("\n")
154
- end
155
-
156
- # Extract width parameter from title attribute
157
- def extract_width_from_title(title)
158
- return nil unless title
159
-
160
- # Match CSS width units: px, em, rem, vw, vh, %, ch
161
- match = title.match(/(\d+(?:\.\d+)?(?:px|em|rem|vw|vh|%|ch))/)
162
- match ? match[1] : nil
163
- end
164
-
165
- # Check if title contains a width value
166
- def contains_width?(title)
167
- return false unless title
168
-
169
- title.match?(/\d+(?:\.\d+)?(?:px|em|rem|vw|vh|%|ch)/)
170
- end
171
- end
172
- end
173
- end
174
- end
@@ -1,53 +0,0 @@
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
@@ -1,44 +0,0 @@
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
@@ -1,19 +0,0 @@
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/carousel_transformer'
12
- require_relative 'transformers/comparison_transformer'
13
- require_relative 'transformers/copy_button_transformer'
14
- require_relative 'transformers/details_transformer'
15
- require_relative 'transformers/dialog_transformer'
16
- require_relative 'transformers/icon_transformer'
17
- require_relative 'transformers/image_dialog_transformer'
18
- require_relative 'transformers/tabs_transformer'
19
- require_relative 'transformers/tag_transformer'