markawesome 0.18.1 → 0.20.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 +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +21 -1
- data/lib/markawesome/plain_markdown_renderer.rb +7 -3
- data/lib/markawesome/transformer.rb +7 -0
- data/lib/markawesome/transformers/accordion_transformer.rb +1 -1
- data/lib/markawesome/transformers/dialog_transformer.rb +36 -24
- data/lib/markawesome/transformers/divider_transformer.rb +125 -0
- data/lib/markawesome/transformers/random_content_transformer.rb +1 -1
- data/lib/markawesome/transformers/stepper_transformer.rb +201 -0
- data/lib/markawesome/transformers.rb +2 -0
- data/lib/markawesome/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '09defacfcfa437cfd7c5406b0705161597633880fac312488a7fa29c3055754d'
|
|
4
|
+
data.tar.gz: 369cd9e0d91dc6a423501367f98e2e54391a04aaebc4cf9da9c79d1c3a4267aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fa42c7657b0abac3554309763a38a75a8c6bc08d39b8a28e85b9fa0b57ea931207705efa0ff194c097582996986871a382b09872508dad2cfe325fc957350efa
|
|
7
|
+
data.tar.gz: 4a509a216fd0ce3aea3238e85dbf8cfea8fc60c7d55e9209e7e9915862b933708afd896a9582cc8c630bbbaf41caf759285f8687a5bff8df45c386d7a058617a
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,35 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.20.0] - 2026-10-09
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Labeled divider (`<wa-divider>` label, Web Awesome 3.13.0+).** New `DividerTransformer` with a mirrored one-line shorthand, `--- label ---`, and a `:::wa-divider params?` alternative whose body is the label (empty for a bare divider). Leading tokens are consumed from the left: `start`/`center`/`end` → `label-placement`, `horizontal`/`vertical` → `orientation`, `icon:name` → a `<wa-icon>` before the label. The rest is the label, rendered as inline Markdown. Output: `<wa-divider orientation="…" label-placement="…"><wa-icon name="…"></wa-icon> Label</wa-divider>`, attributes only when given. Plain `---` and `--- ---` are untouched and stay `<hr>`, and so is any line made only of `|`, `:`, `-` and whitespace, which keeps pipe-less GFM table separators such as `--- | ---` intact. The plain-Markdown renderer degrades it to `---` followed by the label as its own paragraph (icon dropped), with a leading blank line so the rule can't turn a preceding line into a setext heading. Known limitation, shared with every transformer: only backtick code fences are protected, so a `--- x ---` line inside a `~~~` fence is transformed.
|
|
14
|
+
- **Display-only stepper (`<wa-stepper>` / `<wa-step>`, Web Awesome 3.14.0, experimental).** New `StepperTransformer`: a `>>>>>> params?` fence (or `:::wa-stepper params?`) wrapping a Markdown task list. Each unindented list line is a step: `[x]`/`[X]` → `completed`; leading `active`, `loading`, `disabled`, a variant (`neutral`/`brand`/`success`/`warning`/`danger`), an attention (`none`/`pulse`/`bounce`) and `icon:name` (→ `slot="icon"`) are flags; the rest is the label as inline Markdown. Lines indented two or more spaces under a step are joined into `<span slot="description">`. Steps are named `step-1…N` and the container's `active` is the step flagged `active` (last wins), else the first incomplete step, else the last. Container params: `horizontal`/`vertical`/`auto` → `orientation`, `label:"…"` → `label` (HTML-escaped). Interactivity (`clickable`, `linear`, step invokers) is intentionally omitted — the steps have no content panels to navigate between. The `>>>>>>` fence never collides with the `^>>>$` separator of details/dialog/popover/tooltip/random-content. Plain-Markdown degrade: an ordered task list keeping `[x]`/`[ ]`, the current step in bold, and descriptions as indented continuation lines.
|
|
15
|
+
- Both run **first** in the pipeline (right after code-block protection, before layouts), so a divider or stepper can sit inside layouts, cards, callouts, tabs, accordion and dialog bodies — e.g. `--- vertical vs ---` between two buttons in a `::::cluster`. Same position in `PlainMarkdownRenderer::PIPELINE` (new `:divider` / `:stepper` override keys).
|
|
16
|
+
- Manifest coverage now also guards `wa-divider` `orientation`/`label-placement`, `wa-stepper` `orientation` and `wa-step` `variant`/`attention` (`DIVIDER_ATTRIBUTES`, `STEPPER_ATTRIBUTES`, `STEP_ATTRIBUTES`).
|
|
17
|
+
|
|
18
|
+
### Internal
|
|
19
|
+
|
|
20
|
+
- **Refreshed the Web Awesome manifest fixture to 3.14.0** (September 24th, 2026; covers 3.13.0 too). `rake wa:manifest[3.14.0]` regenerated `spec/fixtures/webawesome-enums.json` and `WA_VERSION` moved to `3.14.0`. The diff is **purely additive** — three new tags (`wa-step`, `wa-stepper`, `wa-tag-input`) and a `label-placement` enum on `wa-divider` — with **zero DRIFT and zero GAP** against the enums we accept, so no transformer constant changed. `<wa-accordion>` and `<wa-random-content>` are now **stable** in Web Awesome, so the "experimental" wording was dropped from their transformer comments. Fixture remains byte-identical to `markawesome-js`'s copy. Example-site CDN pins bumped to 3.14.0 in `jekyll-webawesome` and `eleventy-plugin-webawesome`.
|
|
21
|
+
|
|
22
|
+
## [0.19.0] - 2026-08-26
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- **Repeated dialogs no longer share a DOM id.** The id is an MD5 of the trigger text plus body, so two dialogs differing only by a param (e.g. width) collided — and byte-identical ones always did. Both triggers then emitted the same `data-dialog='open …'`, so `getElementById` resolved every one of them to the first dialog and the others were unreachable. Repeats now get a `-2`, `-3`, … suffix, the scheme `generate_popover_id`/`generate_tooltip_id` already used. The first occurrence keeps its bare id, so existing single-dialog output is unchanged.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **Image-dialog trigger buttons now style both `::part(base)` and `::part(button)`.** Web Awesome 3.11.0 gives every component a CSS part named after itself and softly deprecates the generic `base` part — for `<wa-button>` the wrapper part is now `button`, and WA's own manifest marks `base` as *"Deprecated. Use the `button` part instead."* The chrome-stripping `<style>` block emitted for an image trigger (`` under `image_dialog: true`) now lists both selectors in each of its three rules, so the trigger stays chrome-less on 3.10-pinned sites (which have no `button` part) **and** on 3.11+ sites once `base` is eventually removed. Purely additive — no existing selector was dropped. The style block moved into a private `image_button_style` helper; output is byte-identical to `markawesome-js` and now locked by specs in `spec/dialog_transformer_spec.rb` (the emitted selectors were previously untested in either engine).
|
|
31
|
+
|
|
32
|
+
### Internal
|
|
33
|
+
|
|
34
|
+
- **Refreshed the Web Awesome manifest fixture to 3.12.0** (August 2026). `rake wa:manifest[3.12.0]` regenerated `spec/fixtures/webawesome-enums.json` and `WA_VERSION` moved to `3.12.0`. The enum diff is a **single additive line** — `wa-dropdown-item` gained a `target` enum (`_blank`/`_parent`/`_self`/`_top`) alongside its new `href`/`rel`/`download` navigation attributes — on a component that is [explicitly out of scope](ROADMAP.md) for a static-Markdown model, so there is **zero DRIFT and zero GAP** against the enums we accept and no transformer constant changed. Nothing else in WA 3.12.0 touches a component we emit: the other additions are `<wa-data-grid>`'s `filterOptions` (JS-supplied data) and the Figma kit, and every fix/change is internal to Web Awesome's own shadow DOM. Two of those fixes land on our output for free — `<wa-button>` now keeps its accessible name and gains `aria-busy` while loading (we already accept a bare `loading` token in `BUTTON_ATTRIBUTES`), and duplicate landmarks were removed from `<wa-card>`, `<wa-dialog>` and `<wa-drawer>`. Fixture remains byte-identical to `markawesome-js`'s copy. Example-site CDN pins bumped to 3.12.0 in `jekyll-webawesome` and `eleventy-plugin-webawesome`.
|
|
35
|
+
- **Refreshed the Web Awesome manifest fixture to 3.11.0** (July 30th, 2026). `rake wa:manifest[3.11.0]` regenerated `spec/fixtures/webawesome-enums.json` and `WA_VERSION` moved to `3.11.0`. The diff was **purely additive** — four new tags (`wa-otp-input`, `wa-pagination`, and `wa-toast`/`wa-toast-item`, which moved from Pro to Core) — with **zero DRIFT and zero GAP** against the enums we accept, so no transformer constant changed. `IconAttributes::SCHEMA` was separately verified against WA 3.11.0's `IconAnimation` (18 values) and `IconCanvas` types and still matches exactly, as do every slot our transformers emit (`footer`, `icon`, `summary`). Fixture remains byte-identical to `markawesome-js`'s copy.
|
|
36
|
+
- **Web Awesome manifest coverage spec** (`spec/webawesome_manifest_coverage_spec.rb`) — a deterministic guard that checks every enum our transformers accept against a vendored slice of Web Awesome's machine-readable Custom Elements Manifest, pinned to **WA 3.11.0**. For 39 `(constant → wa-tag/attribute)` mappings across 15 components it fails on **DRIFT** (a value we accept that WA no longer lists — exempting the deliberate `callout` `info`→`brand` input alias) and **GAP** (a WA value we don't expose, gated by an `INTENTIONALLY_OMITTED` map that starts empty, so a new WA value turns the build red until adopted or recorded with a reason). 6 entries WA can't describe as an inline enum (`wa-icon` family/variant/animation/canvas are freeform/opaque types; `wa-video` is absent from WA core's manifest) are surfaced as `skip`s, not silently dropped. The fixture `spec/fixtures/webawesome-enums.json` is a **derived artifact** regenerated with `rake wa:manifest[X.Y.Z]` (`script/update_wa_manifest.rb`) — never hand-edited, and byte-identical to `markawesome-js`'s copy. Dev-only (not packaged in the gem); no version bump. See CLAUDE.md → "Web Awesome manifest coverage" for the WA-bump workflow.
|
|
37
|
+
|
|
9
38
|
## [0.18.1] - 2026-07-04
|
|
10
39
|
|
|
11
40
|
### Fixed
|
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@ A framework-agnostic Ruby library that transforms custom Markdown syntax into [W
|
|
|
4
4
|
|
|
5
5
|
Used as the transformation engine for the [jekyll-webawesome](https://github.com/jannewaren/jekyll-webawesome) plugin.
|
|
6
6
|
|
|
7
|
+
> **🔗 See it live — [hosted examples site](https://jannewaren.github.io/jekyll-webawesome/).** Every supported component shown side by side: the Markawesome markdown on the left, the live Web Awesome component it renders on the right. The clearest way to see what this project does.
|
|
8
|
+
|
|
7
9
|
## Features
|
|
8
10
|
|
|
9
11
|
- 🎯 **Framework Agnostic** - Works with any static site generator or Ruby application
|
|
@@ -23,9 +25,11 @@ Used as the transformation engine for the [jekyll-webawesome](https://github.com
|
|
|
23
25
|
| **Copy Button** | `<<<placement? tooltip:full\|copy\|none?` | `:::wa-copy-button placement? tooltip:full\|copy\|none?` | `<wa-copy-button value="content" tooltip-placement="top" tooltip="copy"></wa-copy-button>` |
|
|
24
26
|
| **Details** | `^^^appearance? icon-placement?` | `:::wa-details appearance? icon-placement?` | `<wa-details>content</wa-details>` |
|
|
25
27
|
| **Dialog** | `???params?` | `:::wa-dialog params?` | `<wa-dialog>` with trigger button and content |
|
|
28
|
+
| **Divider** | `--- label ---` (e.g. `--- start icon:star New ---`) | `:::wa-divider params?` (body is the label) | `<wa-divider label-placement="start"><wa-icon name="star"></wa-icon> New</wa-divider>` |
|
|
26
29
|
| **Icon** | `$$$icon-name` | `:::wa-icon icon-name` | `<wa-icon name="icon-name"></wa-icon>` |
|
|
27
30
|
| **Image Dialog** | `` | — | Wraps images in clickable `<wa-dialog>` overlays |
|
|
28
31
|
| **Popover** | `&&¶ms?` or inline `&&&trigger >>> content&&&` | `:::wa-popover params?` | `<wa-popover>` with trigger button and content |
|
|
32
|
+
| **Stepper** | `>>>>>> params?` wrapping a task list | `:::wa-stepper params?` | `<wa-stepper active="step-2"><wa-step name="step-1" completed>…</wa-step>…</wa-stepper>` |
|
|
29
33
|
| **Tab Group** | `++++++` | `:::wa-tabs` | `<wa-tab-group><wa-tab>content</wa-tab></wa-tab-group>` |
|
|
30
34
|
| **Tag** | `@@@brand` | `:::wa-tag brand` | `<wa-tag variant="brand">content</wa-tag>` |
|
|
31
35
|
|
|
@@ -36,6 +40,22 @@ Used as the transformation engine for the [jekyll-webawesome](https://github.com
|
|
|
36
40
|
> listener (e.g. a single global handler that removes `event.target`). The markup
|
|
37
41
|
> we emit is valid either way.
|
|
38
42
|
|
|
43
|
+
> **Labeled divider:** `--- label ---` needs text between the mirrored dashes,
|
|
44
|
+
> so a plain `---` stays an `<hr>`. Leading `start`/`center`/`end`
|
|
45
|
+
> (label placement), `horizontal`/`vertical` (orientation) and `icon:name`
|
|
46
|
+
> tokens are consumed from the left; the rest is the label, as inline Markdown.
|
|
47
|
+
> A line made only of `|`, `:` and `-` (a pipe-less table separator) is left
|
|
48
|
+
> alone. Use `:::wa-divider` when a label must start with one of those words.
|
|
49
|
+
|
|
50
|
+
> **Stepper (display-only):** each unindented list line is a step. `[x]` marks
|
|
51
|
+
> it completed; leading `active`, `loading`, `disabled`, a variant, an attention
|
|
52
|
+
> (`pulse`/`bounce`/`none`) and `icon:name` are flags; lines indented under a
|
|
53
|
+
> step become its description. The active step is the one flagged `active`,
|
|
54
|
+
> else the first incomplete step, else the last. Container params are
|
|
55
|
+
> `horizontal`/`vertical`/`auto` and `label:"…"`. `wa-stepper` is experimental
|
|
56
|
+
> in Web Awesome 3.14.0, and its interactive features (`clickable`, `linear`)
|
|
57
|
+
> are deliberately not exposed.
|
|
58
|
+
|
|
39
59
|
## Layout Utilities
|
|
40
60
|
|
|
41
61
|
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.
|
|
@@ -53,7 +73,7 @@ Layout utilities use `::::` (quadruple colon) syntax to wrap content in CSS layo
|
|
|
53
73
|
|
|
54
74
|
All layouts support these key:value attributes:
|
|
55
75
|
|
|
56
|
-
- `gap:SIZE` — Sets spacing (`0`, `3xs`, `2xs`, `xs`, `s`, `m`, `l`, `xl`, `2xl`, `3xl`)
|
|
76
|
+
- `gap:SIZE` — Sets spacing (`0`, `3xs`, `2xs`, `xs`, `s`, `m`, `l`, `xl`, `2xl`, `3xl`, `4xl`, `5xl`)
|
|
57
77
|
- `align:VALUE` — Align items (`start`, `end`, `center`, `stretch`, `baseline`)
|
|
58
78
|
- `justify:VALUE` — Justify content (`start`, `end`, `center`, `space-between`, `space-around`, `space-evenly`)
|
|
59
79
|
|
|
@@ -25,9 +25,9 @@ module Markawesome
|
|
|
25
25
|
# component without forking the gem.
|
|
26
26
|
#
|
|
27
27
|
# @param component [Symbol] one of :accordion, :callout, :badge, :button,
|
|
28
|
-
# :card, :carousel, :comparison, :copy_button, :details, :dialog,
|
|
29
|
-
# :
|
|
30
|
-
# :tooltip.
|
|
28
|
+
# :card, :carousel, :comparison, :copy_button, :details, :dialog,
|
|
29
|
+
# :divider, :icon, :image_dialog, :layout, :popover, :random_content,
|
|
30
|
+
# :stepper, :tabs, :tag, :tooltip, :tree.
|
|
31
31
|
# @yield [content, options] Proc that receives the full source content
|
|
32
32
|
# and the renderer options; returns the content with that component
|
|
33
33
|
# syntax replaced.
|
|
@@ -44,6 +44,8 @@ module Markawesome
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
PIPELINE = %i[
|
|
47
|
+
divider
|
|
48
|
+
stepper
|
|
47
49
|
layout
|
|
48
50
|
popover
|
|
49
51
|
tooltip
|
|
@@ -68,6 +70,8 @@ module Markawesome
|
|
|
68
70
|
].freeze
|
|
69
71
|
|
|
70
72
|
TRANSFORMER_MAP = {
|
|
73
|
+
divider: DividerTransformer,
|
|
74
|
+
stepper: StepperTransformer,
|
|
71
75
|
layout: LayoutTransformer,
|
|
72
76
|
popover: PopoverTransformer,
|
|
73
77
|
tooltip: TooltipTransformer,
|
|
@@ -10,6 +10,13 @@ module Markawesome
|
|
|
10
10
|
def self.process(content, options = {})
|
|
11
11
|
content, tokens = CodeBlockProtector.protect(content)
|
|
12
12
|
|
|
13
|
+
# Divider and stepper run FIRST: their content is inline-only (a label,
|
|
14
|
+
# a list of one-line steps), so emitting them before any container lets
|
|
15
|
+
# them sit inside layouts, cards, callouts, tabs, accordion and dialog
|
|
16
|
+
# bodies — e.g. `--- vertical vs ---` inside a `::::cluster`.
|
|
17
|
+
content = DividerTransformer.transform(content)
|
|
18
|
+
content = StepperTransformer.transform(content)
|
|
19
|
+
|
|
13
20
|
content = LayoutTransformer.transform(content)
|
|
14
21
|
content = PopoverTransformer.transform(content)
|
|
15
22
|
content = TooltipTransformer.transform(content)
|
|
@@ -23,7 +23,7 @@ module Markawesome
|
|
|
23
23
|
# - expanded -> expanded; disabled -> disabled
|
|
24
24
|
# - icon:name -> <wa-icon slot="icon" name="name"></wa-icon> as first child
|
|
25
25
|
#
|
|
26
|
-
# wa-accordion
|
|
26
|
+
# wa-accordion was added in Web Awesome 3.7 (stable since 3.13). expanded/disabled
|
|
27
27
|
# and the mode/appearance/icon-placement attributes are all static-safe.
|
|
28
28
|
class AccordionTransformer < BaseTransformer
|
|
29
29
|
CONTAINER_ATTRIBUTES = {
|
|
@@ -25,6 +25,10 @@ module Markawesome
|
|
|
25
25
|
primary_regex = /^\?\?\?([^\n]*)$\n(.*?)\n^>>>$\n(.*?)\n^\?\?\?$/m
|
|
26
26
|
alternative_regex = /^:::wa-dialog([^\n]*)$\n(.*?)\n^>>>$\n(.*?)\n^:::$/m
|
|
27
27
|
|
|
28
|
+
# Two dialogs with the same trigger and body hash alike, so suffix repeats
|
|
29
|
+
# (-2, -3, …) to keep ids unique — same scheme as popover/tooltip.
|
|
30
|
+
seen_ids = Hash.new(0)
|
|
31
|
+
|
|
28
32
|
# Define shared transformation logic
|
|
29
33
|
transform_proc = proc do |params_string, button_text, dialog_content|
|
|
30
34
|
button_text = button_text.strip
|
|
@@ -37,7 +41,7 @@ module Markawesome
|
|
|
37
41
|
label, content_without_label = extract_label(dialog_content, button_text)
|
|
38
42
|
|
|
39
43
|
# Generate unique ID based on content
|
|
40
|
-
dialog_id = generate_dialog_id(button_text, dialog_content)
|
|
44
|
+
dialog_id = generate_dialog_id(button_text, dialog_content, seen_ids)
|
|
41
45
|
|
|
42
46
|
# Convert markdown to HTML
|
|
43
47
|
content_html = markdown_to_html(content_without_label)
|
|
@@ -104,11 +108,17 @@ module Markawesome
|
|
|
104
108
|
end
|
|
105
109
|
end
|
|
106
110
|
|
|
107
|
-
# Generate a unique ID for the dialog using MD5 hash
|
|
108
|
-
|
|
111
|
+
# Generate a unique ID for the dialog using MD5 hash.
|
|
112
|
+
# The hash covers the trigger and body only, so two dialogs differing solely
|
|
113
|
+
# by a param (e.g. width) would collide — and identical ones always do. The
|
|
114
|
+
# occurrence counter disambiguates: the first keeps the bare id, later ones
|
|
115
|
+
# get -2, -3, … Mirrors generate_popover_id / generate_tooltip_id.
|
|
116
|
+
def generate_dialog_id(button_text, content, seen_ids)
|
|
109
117
|
hash_input = "#{button_text}#{content}"
|
|
110
118
|
hash = Digest::MD5.hexdigest(hash_input)
|
|
111
|
-
"dialog-#{hash[0..7]}" # Use first 8 characters of hash
|
|
119
|
+
base = "dialog-#{hash[0..7]}" # Use first 8 characters of hash
|
|
120
|
+
occurrence = seen_ids[base] += 1
|
|
121
|
+
occurrence == 1 ? base : "#{base}-#{occurrence}"
|
|
112
122
|
end
|
|
113
123
|
|
|
114
124
|
# Build the complete dialog HTML with trigger button
|
|
@@ -134,26 +144,7 @@ module Markawesome
|
|
|
134
144
|
# Add CSS Parts styling for image buttons to make them invisible
|
|
135
145
|
if is_image_button
|
|
136
146
|
button_id = "#{dialog_id}-btn"
|
|
137
|
-
html
|
|
138
|
-
html << " ##{button_id}::part(base) {"
|
|
139
|
-
html << ' padding: 0;'
|
|
140
|
-
html << ' margin: 0;'
|
|
141
|
-
html << ' border: none;'
|
|
142
|
-
html << ' background: transparent;'
|
|
143
|
-
html << ' box-shadow: none;'
|
|
144
|
-
html << ' color: inherit;'
|
|
145
|
-
html << ' min-width: 0;'
|
|
146
|
-
html << ' height: auto;'
|
|
147
|
-
html << ' }'
|
|
148
|
-
html << " ##{button_id}::part(base):hover {"
|
|
149
|
-
html << ' background: transparent;'
|
|
150
|
-
html << ' border-color: transparent;'
|
|
151
|
-
html << ' }'
|
|
152
|
-
html << " ##{button_id}::part(base):active {"
|
|
153
|
-
html << ' background: transparent;'
|
|
154
|
-
html << ' border-color: transparent;'
|
|
155
|
-
html << ' }'
|
|
156
|
-
html << '</style>'
|
|
147
|
+
html.concat(image_button_style(button_id))
|
|
157
148
|
end
|
|
158
149
|
|
|
159
150
|
# Trigger button
|
|
@@ -175,6 +166,27 @@ module Markawesome
|
|
|
175
166
|
html.join("\n")
|
|
176
167
|
end
|
|
177
168
|
|
|
169
|
+
# Strip the trigger button's chrome so an image trigger reads as a bare image.
|
|
170
|
+
#
|
|
171
|
+
# WA 3.11 deprecated wa-button's generic `base` part in favour of a part named after
|
|
172
|
+
# the component (`button`). `base` still works but is on its way out, so target both —
|
|
173
|
+
# the output then styles correctly on 3.10-pinned and 3.11+ sites.
|
|
174
|
+
def image_button_style(button_id)
|
|
175
|
+
wrapper = lambda do |state|
|
|
176
|
+
" ##{button_id}::part(base)#{state}, ##{button_id}::part(button)#{state} {"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
['<style>', wrapper.call(''),
|
|
180
|
+
' padding: 0;', ' margin: 0;', ' border: none;',
|
|
181
|
+
' background: transparent;', ' box-shadow: none;', ' color: inherit;',
|
|
182
|
+
' min-width: 0;', ' height: auto;', ' }',
|
|
183
|
+
wrapper.call(':hover'),
|
|
184
|
+
' background: transparent;', ' border-color: transparent;', ' }',
|
|
185
|
+
wrapper.call(':active'),
|
|
186
|
+
' background: transparent;', ' border-color: transparent;', ' }',
|
|
187
|
+
'</style>']
|
|
188
|
+
end
|
|
189
|
+
|
|
178
190
|
# Escape HTML entities in text
|
|
179
191
|
def escape_html(text)
|
|
180
192
|
text.gsub('&', '&')
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
5
|
+
require_relative '../icon_slot_parser'
|
|
6
|
+
|
|
7
|
+
module Markawesome
|
|
8
|
+
# Transforms labeled-divider syntax into <wa-divider> elements with a label in
|
|
9
|
+
# the default slot (Web Awesome 3.13.0+).
|
|
10
|
+
#
|
|
11
|
+
# Primary syntax (one line, mirrored dashes):
|
|
12
|
+
# --- or ---
|
|
13
|
+
# --- start Release notes ---
|
|
14
|
+
# --- end icon:star New ---
|
|
15
|
+
# --- icon:quote-left ---
|
|
16
|
+
# --- vertical vs ---
|
|
17
|
+
# Alternative syntax (the body is the label, and may be empty):
|
|
18
|
+
# :::wa-divider start icon:star
|
|
19
|
+
# Release **notes**
|
|
20
|
+
# :::
|
|
21
|
+
#
|
|
22
|
+
# Leading tokens (consumed from the left until the first non-token word):
|
|
23
|
+
# - start, center, end -> label-placement
|
|
24
|
+
# - horizontal, vertical -> orientation
|
|
25
|
+
# - icon:name -> <wa-icon name="name"> before the label
|
|
26
|
+
# The rest of the line is the label, rendered as inline Markdown. To start a
|
|
27
|
+
# label with one of those words, use the alternative syntax.
|
|
28
|
+
#
|
|
29
|
+
# A plain `---` (and `--- ---`) is left alone and stays an <hr>. So is a line
|
|
30
|
+
# made only of `|`, `:`, `-` and whitespace (e.g. a pipe-less GFM table
|
|
31
|
+
# separator such as `--- | ---`).
|
|
32
|
+
class DividerTransformer < BaseTransformer
|
|
33
|
+
DIVIDER_ATTRIBUTES = {
|
|
34
|
+
orientation: %w[horizontal vertical],
|
|
35
|
+
label_placement: %w[start center end]
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
# Content slot: emits <wa-icon name="..."> with no slot attribute.
|
|
39
|
+
ICON_SLOTS = {
|
|
40
|
+
default: 'content',
|
|
41
|
+
slots: %w[content],
|
|
42
|
+
slot_map: { 'content' => 'content' }
|
|
43
|
+
}.freeze
|
|
44
|
+
|
|
45
|
+
PRIMARY_REGEX = /^---[ \t]+(\S[^\n]*?)[ \t]+---[ \t]*$/
|
|
46
|
+
ALTERNATIVE_REGEX = /^:::wa-divider[ \t]*([^\n]*)\n(?:(.*?)\n)?:::[ \t]*$/m
|
|
47
|
+
TABLE_SEPARATOR_REGEX = /\A[|:\-\s]*\z/
|
|
48
|
+
|
|
49
|
+
def self.transform(content)
|
|
50
|
+
content = content.gsub(PRIMARY_REGEX) do |match|
|
|
51
|
+
inner = Regexp.last_match(1)
|
|
52
|
+
next match if inner.match?(TABLE_SEPARATOR_REGEX)
|
|
53
|
+
|
|
54
|
+
lead, label = split_leading_tokens(inner)
|
|
55
|
+
build_html(lead, label)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
content.gsub(ALTERNATIVE_REGEX) do
|
|
59
|
+
params_string = Regexp.last_match(1)
|
|
60
|
+
body = Regexp.last_match(2).to_s
|
|
61
|
+
build_html(params_string, body.strip)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Degrade to a thematic break followed by the label as its own paragraph
|
|
66
|
+
# (icon dropped). The leading blank line stops `---` from turning a
|
|
67
|
+
# preceding paragraph line into a setext heading.
|
|
68
|
+
def self.render_as_markdown(content, _options = {})
|
|
69
|
+
content = content.gsub(PRIMARY_REGEX) do |match|
|
|
70
|
+
inner = Regexp.last_match(1)
|
|
71
|
+
next match if inner.match?(TABLE_SEPARATOR_REGEX)
|
|
72
|
+
|
|
73
|
+
_lead, label = split_leading_tokens(inner)
|
|
74
|
+
markdown_break(label)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
content.gsub(ALTERNATIVE_REGEX) do
|
|
78
|
+
markdown_break(Regexp.last_match(2).to_s.strip)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class << self
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
# Consume leading placement/orientation/icon tokens; the rest is the label.
|
|
86
|
+
def split_leading_tokens(inner)
|
|
87
|
+
tokens = inner.strip.split(/\s+/)
|
|
88
|
+
lead = []
|
|
89
|
+
lead << tokens.shift while tokens.any? && leading_token?(tokens.first)
|
|
90
|
+
[lead.join(' '), tokens.join(' ')]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def leading_token?(token)
|
|
94
|
+
token.start_with?('icon:') || DIVIDER_ATTRIBUTES.values.any? { |values| values.include?(token) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def build_html(params_string, label)
|
|
98
|
+
attributes = AttributeParser.parse(params_string, DIVIDER_ATTRIBUTES)
|
|
99
|
+
icon_result = IconSlotParser.parse(params_string, ICON_SLOTS)
|
|
100
|
+
|
|
101
|
+
attr_parts = []
|
|
102
|
+
attr_parts << "orientation=\"#{attributes[:orientation]}\"" if attributes[:orientation]
|
|
103
|
+
attr_parts << "label-placement=\"#{attributes[:label_placement]}\"" if attributes[:label_placement]
|
|
104
|
+
attrs_string = attr_parts.empty? ? '' : " #{attr_parts.join(' ')}"
|
|
105
|
+
|
|
106
|
+
icon_html = IconSlotParser.to_html(icon_result[:icons], ICON_SLOTS[:slot_map])
|
|
107
|
+
label_html = render_inline(label)
|
|
108
|
+
separator = !icon_html.empty? && !label_html.empty? ? ' ' : ''
|
|
109
|
+
|
|
110
|
+
"<wa-divider#{attrs_string}>#{icon_html}#{separator}#{label_html}</wa-divider>"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Inline Markdown: render, then drop the wrapping <p> (the badge idiom).
|
|
114
|
+
def render_inline(text)
|
|
115
|
+
return '' if text.empty?
|
|
116
|
+
|
|
117
|
+
markdown_to_html(text).strip.sub(%r{\A<p>(.*)</p>\z}m, '\1')
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def markdown_break(label)
|
|
121
|
+
label.empty? ? "\n---" : "\n---\n\n#{label}\n"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -4,7 +4,7 @@ require_relative 'base_transformer'
|
|
|
4
4
|
require_relative '../attribute_parser'
|
|
5
5
|
|
|
6
6
|
module Markawesome
|
|
7
|
-
# Transforms random-content syntax into Web Awesome's
|
|
7
|
+
# Transforms random-content syntax into Web Awesome's
|
|
8
8
|
# <wa-random-content> element, which shows one or more of its direct element
|
|
9
9
|
# children at random (optionally rotating them) and hides the rest — all in
|
|
10
10
|
# WA's own runtime, so the author writes zero JavaScript. Ideal for a "little
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_transformer'
|
|
4
|
+
require_relative '../attribute_parser'
|
|
5
|
+
require_relative '../icon_slot_parser'
|
|
6
|
+
|
|
7
|
+
module Markawesome
|
|
8
|
+
# Transforms stepper syntax into a display-only <wa-stepper> / <wa-step>
|
|
9
|
+
# progress indicator, written as a Markdown task list.
|
|
10
|
+
#
|
|
11
|
+
# Primary syntax:
|
|
12
|
+
# >>>>>> vertical label:"Checkout progress"
|
|
13
|
+
# - [x] Cart
|
|
14
|
+
# - [x] Shipping
|
|
15
|
+
# Standard, 3–5 days
|
|
16
|
+
# - [ ] pulse icon:credit-card Payment
|
|
17
|
+
# - [ ] Review
|
|
18
|
+
# >>>>>>
|
|
19
|
+
# Alternative syntax: :::wa-stepper params? ...same list... :::
|
|
20
|
+
#
|
|
21
|
+
# Container params:
|
|
22
|
+
# - horizontal, vertical, auto -> orientation
|
|
23
|
+
# - label:"text" -> label (accessible name)
|
|
24
|
+
# Steps are unindented list lines (`-`, `*` or `+`):
|
|
25
|
+
# - [x] / [X] marks the step completed; [ ] or no box means not completed
|
|
26
|
+
# - leading flags: active, loading, disabled, a variant (neutral, brand,
|
|
27
|
+
# success, warning, danger), an attention (none, pulse, bounce), and
|
|
28
|
+
# icon:name (slotted into the step's icon slot)
|
|
29
|
+
# - the rest of the line is the label, rendered as inline Markdown
|
|
30
|
+
# - lines indented by two or more spaces under a step are joined into its
|
|
31
|
+
# description slot, rendered as inline Markdown
|
|
32
|
+
#
|
|
33
|
+
# Steps are named step-1..N and the container's `active` points at the step
|
|
34
|
+
# flagged `active` (last one wins), else the first incomplete step, else the
|
|
35
|
+
# last step. Interactivity (`clickable`, `linear`, step invokers) is omitted
|
|
36
|
+
# on purpose: these steps have no content panels, so there is nothing to
|
|
37
|
+
# navigate between without app JavaScript.
|
|
38
|
+
class StepperTransformer < BaseTransformer
|
|
39
|
+
STEPPER_ATTRIBUTES = {
|
|
40
|
+
orientation: %w[horizontal vertical auto]
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
STEP_ATTRIBUTES = {
|
|
44
|
+
variant: %w[neutral brand success warning danger],
|
|
45
|
+
attention: %w[none pulse bounce]
|
|
46
|
+
}.freeze
|
|
47
|
+
|
|
48
|
+
STEP_FLAGS = %w[active loading disabled].freeze
|
|
49
|
+
|
|
50
|
+
ICON_SLOTS = {
|
|
51
|
+
default: 'icon',
|
|
52
|
+
slots: %w[icon],
|
|
53
|
+
slot_map: { 'icon' => 'icon' }
|
|
54
|
+
}.freeze
|
|
55
|
+
|
|
56
|
+
PRIMARY_REGEX = /^>{6}([^\n]*)\n(.*?)\n>{6}[ \t]*$/m
|
|
57
|
+
ALTERNATIVE_REGEX = /^:::wa-stepper[ \t]*([^\n]*)\n(.*?)\n:::[ \t]*$/m
|
|
58
|
+
LABEL_REGEX = /label:(?:"([^"]*)"|'([^']*)')/
|
|
59
|
+
STEP_LINE_REGEX = /\A[-*+][ \t]+(.*)\z/
|
|
60
|
+
CONTINUATION_REGEX = /\A[ \t]{2,}(\S.*)\z/
|
|
61
|
+
CHECKBOX_REGEX = /\A\[([ xX])\](?:[ \t]+(.*))?\z/
|
|
62
|
+
|
|
63
|
+
def self.transform(content)
|
|
64
|
+
transform_proc = proc do |params_string, body|
|
|
65
|
+
label, params_string = extract_label(params_string.to_s)
|
|
66
|
+
attributes = AttributeParser.parse(params_string, STEPPER_ATTRIBUTES)
|
|
67
|
+
steps = parse_steps(body)
|
|
68
|
+
active = active_index(steps)
|
|
69
|
+
|
|
70
|
+
attr_parts = []
|
|
71
|
+
attr_parts << "orientation=\"#{attributes[:orientation]}\"" if attributes[:orientation]
|
|
72
|
+
attr_parts << "label=\"#{escape_html(label)}\"" unless label.empty?
|
|
73
|
+
attr_parts << "active=\"step-#{active + 1}\"" if active
|
|
74
|
+
attrs_string = attr_parts.empty? ? '' : " #{attr_parts.join(' ')}"
|
|
75
|
+
|
|
76
|
+
steps_html = steps.each_with_index.map { |step, index| build_step(step, index) }.join
|
|
77
|
+
"<wa-stepper#{attrs_string}>#{steps_html}</wa-stepper>"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
patterns = dual_syntax_patterns(PRIMARY_REGEX, ALTERNATIVE_REGEX, transform_proc)
|
|
81
|
+
apply_multiple_patterns(content, patterns)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Degrade to an ordered task list: completed steps keep [x], the current
|
|
85
|
+
# step is bold, and a description becomes an indented continuation line.
|
|
86
|
+
def self.render_as_markdown(content, _options = {})
|
|
87
|
+
transform_proc = proc do |_params_string, body|
|
|
88
|
+
steps = parse_steps(body)
|
|
89
|
+
active = active_index(steps)
|
|
90
|
+
|
|
91
|
+
steps.each_with_index.map do |step, index|
|
|
92
|
+
marker = "#{index + 1}. "
|
|
93
|
+
label = index == active && !step[:label].empty? ? "**#{step[:label]}**" : step[:label]
|
|
94
|
+
line = "#{marker}[#{step[:completed] ? 'x' : ' '}]"
|
|
95
|
+
line += " #{label}" unless label.empty?
|
|
96
|
+
line += "\n#{' ' * marker.length}#{step[:description]}" unless step[:description].empty?
|
|
97
|
+
line
|
|
98
|
+
end.join("\n")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
patterns = dual_syntax_patterns(PRIMARY_REGEX, ALTERNATIVE_REGEX, transform_proc)
|
|
102
|
+
apply_multiple_patterns(content, patterns)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class << self
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
# Pull out label:"…" (or label:'…') before the bare tokens are parsed, so
|
|
109
|
+
# words inside the label can't be read as orientation tokens.
|
|
110
|
+
def extract_label(params_string)
|
|
111
|
+
match = LABEL_REGEX.match(params_string)
|
|
112
|
+
return ['', params_string] unless match
|
|
113
|
+
|
|
114
|
+
[(match[1] || match[2]).strip, params_string.sub(LABEL_REGEX, ' ')]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def parse_steps(body)
|
|
118
|
+
steps = []
|
|
119
|
+
body.to_s.each_line do |line|
|
|
120
|
+
line = line.chomp
|
|
121
|
+
if (match = STEP_LINE_REGEX.match(line))
|
|
122
|
+
steps << parse_step(match[1])
|
|
123
|
+
elsif (match = CONTINUATION_REGEX.match(line)) && steps.any?
|
|
124
|
+
steps.last[:description_lines] << match[1].strip
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
steps.each { |step| step[:description] = step.delete(:description_lines).join(' ') }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def parse_step(raw)
|
|
131
|
+
completed = false
|
|
132
|
+
if (match = CHECKBOX_REGEX.match(raw.strip))
|
|
133
|
+
completed = match[1] != ' '
|
|
134
|
+
raw = match[2].to_s
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
tokens = raw.strip.split(/\s+/)
|
|
138
|
+
lead = []
|
|
139
|
+
lead << tokens.shift while tokens.any? && leading_token?(tokens.first)
|
|
140
|
+
lead_string = lead.join(' ')
|
|
141
|
+
|
|
142
|
+
{
|
|
143
|
+
completed: completed,
|
|
144
|
+
flags: lead & STEP_FLAGS,
|
|
145
|
+
attributes: AttributeParser.parse(lead_string, STEP_ATTRIBUTES),
|
|
146
|
+
icons: IconSlotParser.parse(lead_string, ICON_SLOTS)[:icons],
|
|
147
|
+
label: tokens.join(' '),
|
|
148
|
+
description_lines: []
|
|
149
|
+
}
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def leading_token?(token)
|
|
153
|
+
token.start_with?('icon:') ||
|
|
154
|
+
STEP_FLAGS.include?(token) ||
|
|
155
|
+
STEP_ATTRIBUTES.values.any? { |values| values.include?(token) }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# The explicitly active step (last wins), else the first incomplete one,
|
|
159
|
+
# else the last step; nil when there are no steps.
|
|
160
|
+
def active_index(steps)
|
|
161
|
+
return nil if steps.empty?
|
|
162
|
+
|
|
163
|
+
explicit = steps.rindex { |step| step[:flags].include?('active') }
|
|
164
|
+
return explicit if explicit
|
|
165
|
+
|
|
166
|
+
steps.index { |step| !step[:completed] } || (steps.length - 1)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def build_step(step, index)
|
|
170
|
+
attr_parts = ["name=\"step-#{index + 1}\""]
|
|
171
|
+
attr_parts << 'completed' if step[:completed]
|
|
172
|
+
attr_parts << 'loading' if step[:flags].include?('loading')
|
|
173
|
+
attr_parts << 'disabled' if step[:flags].include?('disabled')
|
|
174
|
+
attr_parts << "variant=\"#{step[:attributes][:variant]}\"" if step[:attributes][:variant]
|
|
175
|
+
attr_parts << "attention=\"#{step[:attributes][:attention]}\"" if step[:attributes][:attention]
|
|
176
|
+
|
|
177
|
+
icon_html = IconSlotParser.to_html(step[:icons], ICON_SLOTS[:slot_map])
|
|
178
|
+
description = render_inline(step[:description])
|
|
179
|
+
description_html = description.empty? ? '' : "<span slot=\"description\">#{description}</span>"
|
|
180
|
+
|
|
181
|
+
"<wa-step #{attr_parts.join(' ')}>#{icon_html}#{render_inline(step[:label])}#{description_html}</wa-step>"
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Inline Markdown: render, then drop the wrapping <p> (the badge idiom).
|
|
185
|
+
def render_inline(text)
|
|
186
|
+
return '' if text.empty?
|
|
187
|
+
|
|
188
|
+
markdown_to_html(text).strip.sub(%r{\A<p>(.*)</p>\z}m, '\1')
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def escape_html(text)
|
|
192
|
+
text.to_s
|
|
193
|
+
.gsub('&', '&')
|
|
194
|
+
.gsub('<', '<')
|
|
195
|
+
.gsub('>', '>')
|
|
196
|
+
.gsub('"', '"')
|
|
197
|
+
.gsub("'", ''')
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -15,11 +15,13 @@ require_relative 'transformers/copy_button_transformer'
|
|
|
15
15
|
require_relative 'transformers/date_transformer'
|
|
16
16
|
require_relative 'transformers/details_transformer'
|
|
17
17
|
require_relative 'transformers/dialog_transformer'
|
|
18
|
+
require_relative 'transformers/divider_transformer'
|
|
18
19
|
require_relative 'transformers/icon_transformer'
|
|
19
20
|
require_relative 'transformers/image_dialog_transformer'
|
|
20
21
|
require_relative 'transformers/layout_transformer'
|
|
21
22
|
require_relative 'transformers/popover_transformer'
|
|
22
23
|
require_relative 'transformers/random_content_transformer'
|
|
24
|
+
require_relative 'transformers/stepper_transformer'
|
|
23
25
|
require_relative 'transformers/tabs_transformer'
|
|
24
26
|
require_relative 'transformers/tag_transformer'
|
|
25
27
|
require_relative 'transformers/tooltip_transformer'
|
data/lib/markawesome/version.rb
CHANGED
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
|
+
version: 0.20.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Janne Waren
|
|
@@ -96,11 +96,13 @@ files:
|
|
|
96
96
|
- lib/markawesome/transformers/date_transformer.rb
|
|
97
97
|
- lib/markawesome/transformers/details_transformer.rb
|
|
98
98
|
- lib/markawesome/transformers/dialog_transformer.rb
|
|
99
|
+
- lib/markawesome/transformers/divider_transformer.rb
|
|
99
100
|
- lib/markawesome/transformers/icon_transformer.rb
|
|
100
101
|
- lib/markawesome/transformers/image_dialog_transformer.rb
|
|
101
102
|
- lib/markawesome/transformers/layout_transformer.rb
|
|
102
103
|
- lib/markawesome/transformers/popover_transformer.rb
|
|
103
104
|
- lib/markawesome/transformers/random_content_transformer.rb
|
|
105
|
+
- lib/markawesome/transformers/stepper_transformer.rb
|
|
104
106
|
- lib/markawesome/transformers/tabs_transformer.rb
|
|
105
107
|
- lib/markawesome/transformers/tag_transformer.rb
|
|
106
108
|
- lib/markawesome/transformers/tooltip_transformer.rb
|
|
@@ -130,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
130
132
|
- !ruby/object:Gem::Version
|
|
131
133
|
version: '0'
|
|
132
134
|
requirements: []
|
|
133
|
-
rubygems_version: 4.0.
|
|
135
|
+
rubygems_version: 4.0.20
|
|
134
136
|
specification_version: 4
|
|
135
137
|
summary: Framework-agnostic Markdown to Web Awesome component transformer
|
|
136
138
|
test_files: []
|