markawesome 0.4.0 → 0.6.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: 8a3394f03fff9ca20290650589ba70022b2f8ca71b5581f5d06cb4e06ac83f3f
4
- data.tar.gz: d5af8667249a450c3119641dc38a9f148bbb2b30a1f8c76eae138c3765617fd5
3
+ metadata.gz: 2c03fbaecd8dec3c16fa5e2ddeb07b12d2eb35ad2fbc8389a49b725d77a3c2cf
4
+ data.tar.gz: 6b530bf8caae7b8717411b111e6e3e6f9fb6766373d0b80238cb3fa4ec7f98d6
5
5
  SHA512:
6
- metadata.gz: c29d6692c8cd45f5f2a6a544de3505ff3a9af50a0d36468385f71b38daac30465ff23d15b5d94648f48690fae43d652cf495a4c7ea8d282bc96127a7376e22a3
7
- data.tar.gz: c8134fa3a4a9344efe72ffa5d4a6504763ba9ebce4a4a9c6f7d8b0907de48506ebb9a7ae843bffd0898da5379ba2ebdf9bc100978f2b86add9cec722c4e3281e
6
+ metadata.gz: 6e2b2e8804309e702139068db433be5cbb62132eede58ff45a25d871b721bc2adab4a23d34b820ed8dda26907de2d51ab277b30a13f5d9037e3d26efd123d91f
7
+ data.tar.gz: 8ba50a46d31a068af6ae3d2dc28c2832d26b665e4a006fc74b34f5c59ee0a12596f4f8a7a1a98d7412f0150fe25215d3772521fe6fcaff5f1154c69ac91fc684
data/CHANGELOG.md CHANGED
@@ -4,6 +4,28 @@ 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.6.0] - 2026-02-14
8
+
9
+ ### Changed
10
+
11
+ - **BREAKING: Card header syntax** — Card headers now use `**bold text**` instead of `# heading`. The first bold-only line inside a card block becomes the header slot. This avoids markdownlint warnings about multiple top-level headings (MD025) and incorrect heading levels (MD001), and better reflects that card titles are not semantic document headings.
12
+
13
+ ## [0.5.0] - 2026-02-10
14
+
15
+ ### Added
16
+
17
+ - **LayoutTransformer**: New `::::` (quadruple colon) syntax for wrapping content in Web Awesome CSS layout containers
18
+ - Supports 6 layout types: `grid`, `stack`, `cluster`, `split`, `flank`, `frame`
19
+ - Common attributes: `gap:SIZE`, `align:VALUE`, `justify:VALUE` for all layouts
20
+ - Grid-specific: `min:CSS_VALUE` for custom minimum column size (`--min-column-size`)
21
+ - Split-specific: `row` and `column` direction modifiers
22
+ - Flank-specific: `start`/`end` position modifiers, `size:CSS_VALUE`, `content:PCT`
23
+ - Frame-specific: `landscape`/`portrait`/`square` aspect ratios, `radius:SIZE`
24
+ - Dual syntax: both `::::grid` and `::::wa-grid` work identically
25
+ - CSS value sanitization for user-provided values (strips quotes, angle brackets, semicolons)
26
+ - Inner content is not markdown-converted, allowing component `:::` syntax to be processed by subsequent transformers
27
+ - Runs first in the pipeline so layout containers wrap around component output
28
+
7
29
  ## [0.4.0] - 2026-02-09
8
30
 
9
31
  ### Added
data/README.md CHANGED
@@ -10,22 +10,52 @@ Used as the transformation engine for the [jekyll-webawesome](https://github.com
10
10
  - 🚀 **Simple Syntax** - Clean, intuitive Markdown extensions
11
11
  - ⚙️ **Configurable** - Customize icons, variants, and component behavior
12
12
 
13
- ## Supported components
13
+ ## Supported Components
14
14
 
15
15
  | Component | Primary Syntax | Alternative Syntax | HTML Output |
16
16
  |-----------|----------------|-------------------|-------------|
17
17
  | **Badge** | `!!!variant` | `:::wa-badge variant` | `<wa-badge variant="brand">content</wa-badge>` |
18
18
  | **Button** | `%%%variant` | `:::wa-button variant` | `<wa-button variant="brand" href="url">text</wa-button>` or `<wa-button variant="brand">text</wa-button>` |
19
- | **Callouts** | `:::info` | `:::wa-callout info` | `<wa-callout variant="brand"><wa-icon name="circle-info"></wa-icon>content</wa-callout>` |
19
+ | **Callout** | `:::info` | `:::wa-callout info` | `<wa-callout variant="brand"><wa-icon ...></wa-icon>content</wa-callout>` |
20
20
  | **Card** | `===` | `:::wa-card` | `<wa-card>content</wa-card>` |
21
21
  | **Carousel** | `~~~~~~` | `:::wa-carousel` | `<wa-carousel>` with carousel items |
22
22
  | **Comparison** | `\|\|\|` or `\|\|\|25` | `:::wa-comparison` or `:::wa-comparison 25` | `<wa-comparison>` with before/after slots |
23
23
  | **Copy Button** | `<<<` | `:::wa-copy-button` | `<wa-copy-button value="content">content</wa-copy-button>` |
24
- | **Details** | `^^^appearance? icon-placement?` | `:::wa-details appearance? icon-placement?` | `<wa-details appearance="..." icon-placement="...">content</wa-details>` |
24
+ | **Details** | `^^^appearance? icon-placement?` | `:::wa-details appearance? icon-placement?` | `<wa-details>content</wa-details>` |
25
25
  | **Dialog** | `???params?` | `:::wa-dialog params?` | `<wa-dialog>` with trigger button and content |
26
+ | **Icon** | `$$$icon-name` | `:::wa-icon icon-name` | `<wa-icon name="icon-name"></wa-icon>` |
27
+ | **Image Dialog** | `![alt](url)` | — | Wraps images in clickable `<wa-dialog>` overlays |
26
28
  | **Tab Group** | `++++++` | `:::wa-tabs` | `<wa-tab-group><wa-tab>content</wa-tab></wa-tab-group>` |
27
29
  | **Tag** | `@@@brand` | `:::wa-tag brand` | `<wa-tag variant="brand">content</wa-tag>` |
28
30
 
31
+ ## Layout Utilities
32
+
33
+ Layout utilities use `::::` (quadruple colon) syntax to wrap content in CSS layout containers. Inner content is not markdown-converted, so component `:::` syntax inside layouts works normally.
34
+
35
+ | Layout | Primary Syntax | Alternative Syntax | HTML Output |
36
+ |--------|----------------|-------------------|-------------|
37
+ | **Grid** | `::::grid` | `::::wa-grid` | `<div class="wa-grid">content</div>` |
38
+ | **Stack** | `::::stack` | `::::wa-stack` | `<div class="wa-stack">content</div>` |
39
+ | **Cluster** | `::::cluster` | `::::wa-cluster` | `<div class="wa-cluster">content</div>` |
40
+ | **Split** | `::::split` | `::::wa-split` | `<div class="wa-split">content</div>` |
41
+ | **Flank** | `::::flank` | `::::wa-flank` | `<div class="wa-flank">content</div>` |
42
+ | **Frame** | `::::frame` | `::::wa-frame` | `<div class="wa-frame">content</div>` |
43
+
44
+ ### Common layout attributes
45
+
46
+ All layouts support these key:value attributes:
47
+
48
+ - `gap:SIZE` — Sets spacing (`0`, `3xs`, `2xs`, `xs`, `s`, `m`, `l`, `xl`, `2xl`, `3xl`)
49
+ - `align:VALUE` — Align items (`start`, `end`, `center`, `stretch`, `baseline`)
50
+ - `justify:VALUE` — Justify content (`start`, `end`, `center`, `space-between`, `space-around`, `space-evenly`)
51
+
52
+ ### Layout-specific attributes
53
+
54
+ - **Grid**: `min:CSS_VALUE` — Minimum column size (e.g., `min:200px`)
55
+ - **Split**: `row` or `column` — Direction modifier
56
+ - **Flank**: `start` or `end` — Position modifier; `size:CSS_VALUE`, `content:PCT`
57
+ - **Frame**: `landscape`, `portrait`, or `square` — Aspect ratio; `radius:SIZE` (`s`, `m`, `l`, `pill`, `circle`, `square`)
58
+
29
59
  ## Installation
30
60
 
31
61
  Add this line to your application's Gemfile:
@@ -7,6 +7,7 @@ module Markawesome
7
7
  # Main transformer that orchestrates all component transformers
8
8
  class Transformer
9
9
  def self.process(content, options = {})
10
+ content = LayoutTransformer.transform(content)
10
11
  content = BadgeTransformer.transform(content)
11
12
  content = ButtonTransformer.transform(content)
12
13
  content = CalloutTransformer.transform(content)
@@ -10,6 +10,7 @@ module Markawesome
10
10
  # Supported attributes:
11
11
  # appearance: outlined (default), filled, filled-outlined, plain, accent
12
12
  # orientation: vertical (default), horizontal
13
+ # Card header: first **bold** line (not a heading) becomes the header slot
13
14
  class CardTransformer < BaseTransformer
14
15
  CARD_ATTRIBUTES = {
15
16
  appearance: %w[outlined filled filled-outlined plain accent],
@@ -57,11 +58,11 @@ module Markawesome
57
58
  content = content.sub(/^!\[([^\]]*)\]\(([^)]+)\)\n?/, '')
58
59
  end
59
60
 
60
- # Extract first heading as header
61
- if content.match(/^# (.+)$/)
61
+ # Extract first bold line as header
62
+ if content.match(/^\*\*(.+)\*\*$/)
62
63
  parts[:header] = ::Regexp.last_match(1).strip
63
- # Remove the heading from content
64
- content = content.sub(/^# .+\n?/, '')
64
+ # Remove the bold line from content
65
+ content = content.sub(/^\*\*(.+)\*\*\n?/, '')
65
66
  end
66
67
 
67
68
  # Extract trailing buttons/links as footer
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_transformer'
4
+
5
+ module Markawesome
6
+ # Transforms layout syntax into CSS layout utility containers
7
+ # Primary syntax: ::::type params?\ncontent\n::::
8
+ # Alternative syntax: ::::wa-type params?\ncontent\n::::
9
+ # Supported types: grid, stack, cluster, split, flank, frame
10
+ #
11
+ # Common attributes (all layouts):
12
+ # gap:SIZE - wa-gap-{SIZE} class (0, 3xs, 2xs, xs, s, m, l, xl, 2xl, 3xl)
13
+ # align:VALUE - wa-align-items-{VALUE} class (start, end, center, stretch, baseline)
14
+ # justify:VALUE - wa-justify-content-{VALUE} class (start, end, center, space-between, space-around, space-evenly)
15
+ #
16
+ # Grid-specific: min:CSS_VALUE - sets --min-column-size style
17
+ # Split-specific: row, column modifiers
18
+ # Flank-specific: start, end modifiers; size:CSS_VALUE, content:PCT
19
+ # Frame-specific: landscape, portrait, square modifiers; radius:SIZE
20
+ class LayoutTransformer < BaseTransformer
21
+ VALID_GAPS = %w[0 3xs 2xs xs s m l xl 2xl 3xl].freeze
22
+ VALID_ALIGNS = %w[start end center stretch baseline].freeze
23
+ VALID_JUSTIFIES = %w[start end center space-between space-around space-evenly].freeze
24
+ VALID_RADII = %w[s m l pill circle square].freeze
25
+
26
+ KEYWORD_MODIFIERS = {
27
+ split: %w[row column],
28
+ flank: %w[start end],
29
+ frame: %w[landscape portrait square]
30
+ }.freeze
31
+
32
+ COMMON_KEY_CLASS_MAP = {
33
+ 'gap' => ->(v) { "wa-gap-#{v}" if VALID_GAPS.include?(v) },
34
+ 'align' => ->(v) { "wa-align-items-#{v}" if VALID_ALIGNS.include?(v) },
35
+ 'justify' => ->(v) { "wa-justify-content-#{v}" if VALID_JUSTIFIES.include?(v) }
36
+ }.freeze
37
+
38
+ def self.transform(content)
39
+ primary_regex = /^::::(grid|stack|cluster|split|flank|frame)[ \t]*([^\n]*)\n(.*?)\n::::/m
40
+ alternative_regex = /^::::wa-(grid|stack|cluster|split|flank|frame)[ \t]*([^\n]*)\n(.*?)\n::::/m
41
+
42
+ transform_proc = proc do |type, params_string, inner_content|
43
+ classes, styles = build_attributes(type, params_string)
44
+ build_html(classes, styles, inner_content)
45
+ end
46
+
47
+ patterns = dual_syntax_patterns(primary_regex, alternative_regex, transform_proc)
48
+ apply_multiple_patterns(content, patterns)
49
+ end
50
+
51
+ class << self
52
+ private
53
+
54
+ def build_attributes(type, params_string)
55
+ classes = ["wa-#{type}"]
56
+ styles = []
57
+
58
+ return [classes, styles] if params_string.nil? || params_string.strip.empty?
59
+
60
+ tokens = params_string.strip.split(/\s+/)
61
+
62
+ tokens.each do |token|
63
+ process_token(type, token, classes, styles)
64
+ end
65
+
66
+ [classes, styles]
67
+ end
68
+
69
+ def process_token(type, token, classes, styles)
70
+ if token.include?(':')
71
+ process_key_value(type, token, classes, styles)
72
+ else
73
+ process_keyword(type, token, classes)
74
+ end
75
+ end
76
+
77
+ def process_key_value(type, token, classes, styles)
78
+ key, value = token.split(':', 2)
79
+ return if value.nil? || value.empty?
80
+
81
+ if COMMON_KEY_CLASS_MAP.key?(key)
82
+ css_class = COMMON_KEY_CLASS_MAP[key].call(value)
83
+ classes << css_class if css_class
84
+ else
85
+ process_type_specific_key_value(type, key, value, classes, styles)
86
+ end
87
+ end
88
+
89
+ def process_type_specific_key_value(type, key, value, classes, styles)
90
+ case key
91
+ when 'min'
92
+ styles << "--min-column-size: #{sanitize_css(value)}" if type == 'grid'
93
+ when 'size'
94
+ styles << "--flank-size: #{sanitize_css(value)}" if type == 'flank'
95
+ when 'content'
96
+ styles << "--content-percentage: #{sanitize_css(value)}" if type == 'flank'
97
+ when 'radius'
98
+ classes << "wa-border-radius-#{value}" if type == 'frame' && VALID_RADII.include?(value)
99
+ end
100
+ end
101
+
102
+ def process_keyword(type, token, classes)
103
+ modifiers = KEYWORD_MODIFIERS[type.to_sym]
104
+ return unless modifiers&.include?(token)
105
+
106
+ classes[0] = "wa-#{type}:#{token}"
107
+ end
108
+
109
+ def sanitize_css(value)
110
+ value.gsub(/["'<>;]/, '')
111
+ end
112
+
113
+ def build_html(classes, styles, inner_content)
114
+ attr_parts = ["class=\"#{classes.join(' ')}\""]
115
+ attr_parts << "style=\"#{styles.join('; ')}\"" unless styles.empty?
116
+
117
+ "<div #{attr_parts.join(' ')}>\n#{inner_content}\n</div>"
118
+ end
119
+ end
120
+ end
121
+ end
@@ -15,5 +15,6 @@ require_relative 'transformers/details_transformer'
15
15
  require_relative 'transformers/dialog_transformer'
16
16
  require_relative 'transformers/icon_transformer'
17
17
  require_relative 'transformers/image_dialog_transformer'
18
+ require_relative 'transformers/layout_transformer'
18
19
  require_relative 'transformers/tabs_transformer'
19
20
  require_relative 'transformers/tag_transformer'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Markawesome
4
- VERSION = '0.4.0'
4
+ VERSION = '0.6.0'
5
5
  end
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.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janne Waren
@@ -107,6 +107,7 @@ files:
107
107
  - lib/markawesome/transformers/dialog_transformer.rb
108
108
  - lib/markawesome/transformers/icon_transformer.rb
109
109
  - lib/markawesome/transformers/image_dialog_transformer.rb
110
+ - lib/markawesome/transformers/layout_transformer.rb
110
111
  - lib/markawesome/transformers/tabs_transformer.rb
111
112
  - lib/markawesome/transformers/tag_transformer.rb
112
113
  - lib/markawesome/version.rb