jekyll-webmentions-static 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6bdd84c741b25702134b381a11ff8b6e5388a8a949f4531e562bedf6d3032872
4
+ data.tar.gz: ed687e8bd29cdc3bb11e56f07cdaf11213bfe23266b7b1d657ac6225fd39c2b1
5
+ SHA512:
6
+ metadata.gz: f0fe721abe9b8af701b4b11569c85e72c8f8a47cd8fd15ff678db13381e666afd0d622af9851bf3ea1150b5f16b3742a33fc1fdd733f107d627a8d84737f3cfc
7
+ data.tar.gz: 84108908d211adfd0d69c3a04ed5c0855a52959ce548fe82f10327fe8cef3a1d57c63d948ede793bd9fe8a95a67dff9f445060675d79035cfae712a5ac4a8e49
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jason Chance
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # jekyll-webmentions-static
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/jekyll-webmentions-static.svg)](https://rubygems.org/gems/jekyll-webmentions-static)
4
+ [![Tests](https://github.com/jchance/jekyll-webmentions-static/actions/workflows/test.yml/badge.svg)](https://github.com/jchance/jekyll-webmentions-static/actions)
5
+
6
+ Fetch and render [Webmention.io](https://webmention.io) mentions as static HTML at Jekyll build time. No JavaScript, no runtime API calls — pure static output.
7
+
8
+ ---
9
+
10
+ ## How it works
11
+
12
+ During `jekyll build`, the plugin:
13
+
14
+ 1. Fetches all webmentions for your domain from the Webmention.io API.
15
+ 2. Groups them by target URL and stores them in `site.data["webmentions"]`.
16
+ 3. Injects matching mentions into each post's `page.webmentions` array.
17
+ 4. Caches the API response locally so repeated builds don't hammer the API.
18
+
19
+ ---
20
+
21
+ ## Installation
22
+
23
+ Add to your site's `Gemfile`:
24
+
25
+ ```ruby
26
+ gem "jekyll-webmentions-static"
27
+ ```
28
+
29
+ And to `_config.yml`:
30
+
31
+ ```yaml
32
+ plugins:
33
+ - jekyll-webmentions-static
34
+ ```
35
+
36
+ Then run:
37
+
38
+ ```sh
39
+ bundle install
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Configuration
45
+
46
+ Add to `_config.yml`:
47
+
48
+ ```yaml
49
+ webmentions:
50
+ token: YOUR_TOKEN # or use WEBMENTION_TOKEN env var (preferred for CI/CD)
51
+ cache: true # default: true — cache API responses locally
52
+ cache_ttl: 3600 # default: 3600 — seconds before the cache is considered stale
53
+ ```
54
+
55
+ ### Getting a token
56
+
57
+ 1. Go to [webmention.io](https://webmention.io) and sign in with your domain (using GitHub, email `rel=me`, or another IndieAuth provider).
58
+ 2. Once authenticated, your API token is shown in your [settings](https://webmention.io/settings).
59
+ 3. Add the token to your `_config.yml` **or** (strongly preferred) set it as an environment variable:
60
+
61
+ ```sh
62
+ export WEBMENTION_TOKEN=your-token-here
63
+ ```
64
+
65
+ The environment variable always takes precedence over the config file value. This keeps secrets out of version control.
66
+
67
+ ### Add the `rel="webmention"` link to your `<head>`
68
+
69
+ Webmention.io needs to know your site is using it. Add these two tags to your HTML `<head>`:
70
+
71
+ ```html
72
+ <link rel="webmention" href="https://webmention.io/yourdomain.com/webmention" />
73
+ <link rel="pingback" href="https://webmention.io/xmlrpc" />
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Usage in templates
79
+
80
+ ### Option 1 — Liquid tag (built-in template)
81
+
82
+ Place `{% webmentions %}` anywhere in a post or page layout to render a minimal, accessible webmentions section:
83
+
84
+ ```liquid
85
+ {% webmentions %}
86
+ ```
87
+
88
+ This renders likes, reposts, and bookmarks as counts, and replies as individual cards with author name, avatar, and content.
89
+
90
+ ### Option 2 — Manual template with `page.webmentions`
91
+
92
+ For full control over markup, iterate over `page.webmentions` directly:
93
+
94
+ ```liquid
95
+ {% if page.webmentions %}
96
+ <section class="webmentions">
97
+ {% assign likes = page.webmentions | where: "type", "like-of" %}
98
+ {% assign replies = page.webmentions | where: "type", "in-reply-to" %}
99
+
100
+ {% if likes.size > 0 %}
101
+ <p>{{ likes.size }} like{{ likes.size | plural: "", "s" }}</p>
102
+ {% endif %}
103
+
104
+ {% for mention in replies %}
105
+ <article>
106
+ <strong>{{ mention.author.name }}</strong>
107
+ {% if mention.content.text %}
108
+ <p>{{ mention.content.text }}</p>
109
+ {% endif %}
110
+ <a href="{{ mention.url }}">{{ mention.published | date: "%B %-d, %Y" }}</a>
111
+ </article>
112
+ {% endfor %}
113
+ </section>
114
+ {% endif %}
115
+ ```
116
+
117
+ ### Accessing all webmentions in any template
118
+
119
+ The full hash is available at `site.data.webmentions`:
120
+
121
+ ```liquid
122
+ {% assign mentions = site.data.webmentions[page.url | prepend: site.url] %}
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Caching
128
+
129
+ The plugin writes fetched data to `_webmentions_cache.json` in your site source directory. Subsequent builds within the TTL window (default: 1 hour) read from this file instead of calling the API.
130
+
131
+ **Add the cache file to your `.gitignore`:**
132
+
133
+ ```
134
+ _webmentions_cache.json
135
+ ```
136
+
137
+ To force a fresh fetch, delete the cache file or set `cache: false` in your config.
138
+
139
+ ---
140
+
141
+ ## GitHub Actions setup
142
+
143
+ Store your token as a [repository secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets) named `WEBMENTION_TOKEN`, then expose it in your workflow:
144
+
145
+ ```yaml
146
+ - name: Build Jekyll site
147
+ run: bundle exec jekyll build
148
+ env:
149
+ WEBMENTION_TOKEN: ${{ secrets.WEBMENTION_TOKEN }}
150
+ ```
151
+
152
+ Because the cache file is in `.gitignore`, each CI build fetches fresh data. To cache across CI runs, add `_webmentions_cache.json` to your Actions cache key. Without CI caching you'll make one API call per build, which is fine for most sites.
153
+
154
+ ### Keeping mentions fresh with a scheduled rebuild
155
+
156
+ Webmentions only update when your site is rebuilt. If your site doesn't publish frequently, add a `schedule` trigger to your GitHub Pages workflow so mentions stay current automatically:
157
+
158
+ ```yaml
159
+ on:
160
+ push:
161
+ branches: [main]
162
+ schedule:
163
+ - cron: '0 */6 * * *' # rebuild every 6 hours
164
+ ```
165
+
166
+ Adjust the cron interval to taste — daily (`0 0 * * *`) is reasonable for most sites. Each scheduled run fetches any new mentions from Webmention.io and publishes the updated site.
167
+
168
+ ---
169
+
170
+ ## Webmention types
171
+
172
+ | `type` | Meaning |
173
+ |-----------------|----------------------------------|
174
+ | `like-of` | Someone liked your post |
175
+ | `repost-of` | Someone reposted / boosted it |
176
+ | `in-reply-to` | A reply with content |
177
+ | `bookmark-of` | Someone bookmarked it |
178
+ | `mention-of` | A generic link mention |
179
+
180
+ ---
181
+
182
+ ## Styling
183
+
184
+ The `{% webmentions %}` tag outputs unstyled semantic HTML. All elements use BEM class names:
185
+
186
+ ```css
187
+ /* Outer section */
188
+ .webmentions { }
189
+
190
+ /* Reaction count pills */
191
+ .webmentions__counts { }
192
+ .webmentions__likes { }
193
+ .webmentions__reposts { }
194
+ .webmentions__bookmarks { }
195
+
196
+ /* Replies section */
197
+ .webmentions__replies { }
198
+ .webmentions__replies-heading { }
199
+
200
+ /* Individual reply card */
201
+ .webmention__reply { }
202
+ .webmention__author { }
203
+ .webmention__avatar { } /* circular avatar img */
204
+ .webmention__author-name { } /* linked name */
205
+ .webmention__date { }
206
+ .webmention__content { }
207
+ ```
208
+
209
+ The count pills include a `title` attribute (e.g. `title="2 likes"`) for native browser tooltip text on hover.
210
+
211
+ ---
212
+
213
+ ## Requirements
214
+
215
+ - Ruby >= 2.7
216
+ - Jekyll >= 4.0
217
+ - No additional gems required — uses Ruby's built-in `net/http`
218
+
219
+ ---
220
+
221
+ ## License
222
+
223
+ [MIT](LICENSE.txt) © 2025 Jason Chance
@@ -0,0 +1,73 @@
1
+ require "jekyll"
2
+ require_relative "../jekyll-webmentions-static/fetcher"
3
+
4
+ module Jekyll
5
+ class WebmentionsGenerator < Jekyll::Generator
6
+ safe true
7
+ priority :low
8
+
9
+ def generate(site)
10
+ token = resolve_token(site)
11
+
12
+ if token.nil? || token.strip.empty?
13
+ Jekyll.logger.info "Webmentions:", "No token configured — skipping"
14
+ return
15
+ end
16
+
17
+ domain = extract_domain(site.config["url"])
18
+
19
+ if domain.nil? || domain.empty?
20
+ Jekyll.logger.warn "Webmentions:", "site.url not set — skipping"
21
+ return
22
+ end
23
+
24
+ cfg = site.config.fetch("webmentions", {})
25
+ cache = cfg.fetch("cache", true)
26
+ cache_ttl = cfg.fetch("cache_ttl", Jekyll::WebmentionsStatic::Fetcher::DEFAULT_TTL)
27
+
28
+ fetcher = Jekyll::WebmentionsStatic::Fetcher.new(
29
+ token: token,
30
+ domain: domain,
31
+ site_source: site.source,
32
+ cache: cache,
33
+ cache_ttl: cache_ttl
34
+ )
35
+
36
+ grouped = fetcher.fetch_and_group
37
+
38
+ site.data["webmentions"] = grouped
39
+
40
+ inject_into_posts(site, grouped)
41
+
42
+ total = grouped.values.sum(&:length)
43
+ Jekyll.logger.info "Webmentions:", "Loaded #{total} mention(s) across #{grouped.keys.length} URL(s)"
44
+ end
45
+
46
+ private
47
+
48
+ def resolve_token(site)
49
+ env_token = ENV["WEBMENTION_TOKEN"]
50
+ return env_token unless env_token.nil? || env_token.strip.empty?
51
+
52
+ site.config.dig("webmentions", "token")
53
+ end
54
+
55
+ def extract_domain(url)
56
+ return nil if url.nil? || url.strip.empty?
57
+
58
+ url.strip
59
+ .sub(%r{\Ahttps?://}, "")
60
+ .sub(%r{/+\z}, "")
61
+ end
62
+
63
+ def inject_into_posts(site, grouped)
64
+ base_url = site.config["url"].to_s.sub(%r{/+\z}, "")
65
+
66
+ site.posts.docs.each do |post|
67
+ full_url = base_url + post.url
68
+ mentions = grouped[full_url]
69
+ post.data["webmentions"] = mentions if mentions
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,78 @@
1
+ require "jekyll"
2
+
3
+ module Jekyll
4
+ class WebmentionsTag < Liquid::Tag
5
+
6
+ ICON_HEART = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>'.freeze
7
+ ICON_REPOST = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"/></svg>'.freeze
8
+ ICON_BOOKMARK = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2z"/></svg>'.freeze
9
+
10
+ def render(context)
11
+ page = context["page"]
12
+ mentions = page["webmentions"]
13
+
14
+ return "" if mentions.nil? || mentions.empty?
15
+
16
+ likes = mentions.select { |m| m["type"] == "like-of" }
17
+ reposts = mentions.select { |m| m["type"] == "repost-of" }
18
+ replies = mentions.select { |m| m["type"] == "in-reply-to" }
19
+ bookmarks = mentions.select { |m| m["type"] == "bookmark-of" }
20
+
21
+ html = []
22
+ html << '<section class="webmentions" aria-label="Webmentions">'
23
+
24
+ if likes.any? || reposts.any? || bookmarks.any?
25
+ html << ' <div class="webmentions__counts">'
26
+ html << " <span class=\"webmentions__likes\" title=\"#{likes.length} #{likes.length == 1 ? "like" : "likes"}\">#{ICON_HEART} #{likes.length}</span>" if likes.any?
27
+ html << " <span class=\"webmentions__reposts\" title=\"#{reposts.length} #{reposts.length == 1 ? "repost" : "reposts"}\">#{ICON_REPOST} #{reposts.length}</span>" if reposts.any?
28
+ html << " <span class=\"webmentions__bookmarks\" title=\"#{bookmarks.length} #{bookmarks.length == 1 ? "bookmark" : "bookmarks"}\">#{ICON_BOOKMARK} #{bookmarks.length}</span>" if bookmarks.any?
29
+ html << " </div>"
30
+ end
31
+
32
+ if replies.any?
33
+ html << ' <div class="webmentions__replies">'
34
+ html << " <h3 class=\"webmentions__replies-heading\">Replies</h3>"
35
+ replies.each do |reply|
36
+ author = reply["author"] || {}
37
+ name = CGI.escapeHTML(author["name"].to_s.then { |n| n.empty? ? "Anonymous" : n })
38
+ photo = author["photo"]
39
+ url = CGI.escapeHTML(reply["url"].to_s)
40
+ text = reply.dig("content", "text").to_s
41
+ content = CGI.escapeHTML(text.length > 300 ? "#{text[0, 300]}…" : text)
42
+ date = format_date(reply["published"])
43
+
44
+ html << ' <article class="webmention__reply">'
45
+ html << ' <header class="webmention__author">'
46
+ if photo
47
+ html << " <img src=\"#{CGI.escapeHTML(photo)}\" alt=\"#{name}\" class=\"webmention__avatar\" width=\"40\" height=\"40\" loading=\"lazy\">"
48
+ end
49
+ if url.empty?
50
+ html << " <span class=\"webmention__author-name\">#{name}</span>"
51
+ else
52
+ html << " <a href=\"#{url}\" class=\"webmention__author-name\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#{name}</a>"
53
+ end
54
+ html << " <time class=\"webmention__date\" datetime=\"#{CGI.escapeHTML(reply["published"].to_s)}\">#{date}</time>" if date
55
+ html << " </header>"
56
+ html << " <p class=\"webmention__content\">#{content}</p>" unless content.empty?
57
+ html << " </article>"
58
+ end
59
+ html << " </div>"
60
+ end
61
+
62
+ html << "</section>"
63
+ html.join("\n")
64
+ end
65
+
66
+ private
67
+
68
+ def format_date(iso_str)
69
+ return nil if iso_str.nil? || iso_str.strip.empty?
70
+
71
+ Time.parse(iso_str).strftime("%B %-d, %Y")
72
+ rescue ArgumentError
73
+ nil
74
+ end
75
+ end
76
+ end
77
+
78
+ Liquid::Template.register_tag("webmentions", Jekyll::WebmentionsTag)
@@ -0,0 +1,98 @@
1
+ require "net/http"
2
+ require "uri"
3
+ require "json"
4
+ require "time"
5
+
6
+ module Jekyll
7
+ module WebmentionsStatic
8
+ class Fetcher
9
+ ENDPOINT = "https://webmention.io/api/mentions.jf2"
10
+ DEFAULT_TTL = 3600
11
+
12
+ def initialize(token:, domain:, site_source:, cache: true, cache_ttl: DEFAULT_TTL)
13
+ @token = token
14
+ @domain = domain
15
+ @site_source = site_source
16
+ @cache = cache
17
+ @cache_ttl = cache_ttl.to_i
18
+ @cache_path = File.join(site_source, "_webmentions_cache.json")
19
+ end
20
+
21
+ # Returns a hash keyed by wm-target URL → Array of mention hashes.
22
+ def fetch_and_group
23
+ raw = load_from_cache || fetch_from_api
24
+ group_by_target(raw)
25
+ end
26
+
27
+ private
28
+
29
+ def cache_valid?
30
+ return false unless @cache && File.exist?(@cache_path)
31
+
32
+ age = Time.now - File.mtime(@cache_path)
33
+ age < @cache_ttl
34
+ end
35
+
36
+ def load_from_cache
37
+ return nil unless cache_valid?
38
+
39
+ Jekyll.logger.info "Webmentions:", "Using cached data (#{@cache_path})"
40
+ JSON.parse(File.read(@cache_path))
41
+ rescue JSON::ParserError => e
42
+ Jekyll.logger.warn "Webmentions:", "Cache parse error: #{e.message} — refetching"
43
+ nil
44
+ end
45
+
46
+ def fetch_from_api
47
+ uri = URI(ENDPOINT)
48
+ uri.query = URI.encode_www_form(
49
+ "token" => @token,
50
+ "domain" => @domain,
51
+ "per-page" => 500
52
+ )
53
+
54
+ Jekyll.logger.info "Webmentions:", "Fetching from #{uri}"
55
+
56
+ response = Net::HTTP.get_response(uri)
57
+
58
+ unless response.is_a?(Net::HTTPSuccess)
59
+ Jekyll.logger.warn "Webmentions:", "HTTP #{response.code} — skipping"
60
+ return []
61
+ end
62
+
63
+ data = JSON.parse(response.body)
64
+ items = data["items"] || []
65
+
66
+ write_cache(items)
67
+ items
68
+ rescue SocketError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Net::OpenTimeout,
69
+ Net::ReadTimeout, OpenSSL::SSL::SSLError => e
70
+ Jekyll.logger.warn "Webmentions:", "Network error: #{e.message} — skipping"
71
+ []
72
+ rescue JSON::ParserError => e
73
+ Jekyll.logger.warn "Webmentions:", "JSON parse error: #{e.message} — skipping"
74
+ []
75
+ end
76
+
77
+ def write_cache(items)
78
+ return unless @cache
79
+
80
+ File.write(@cache_path, JSON.generate(items))
81
+ Jekyll.logger.info "Webmentions:", "Cache written to #{@cache_path}"
82
+ rescue Errno::EACCES, Errno::EROFS => e
83
+ Jekyll.logger.warn "Webmentions:", "Could not write cache: #{e.message}"
84
+ end
85
+
86
+ def group_by_target(items)
87
+ grouped = {}
88
+ items.each do |item|
89
+ target = item["wm-target"]
90
+ next if target.nil? || target.strip.empty?
91
+
92
+ (grouped[target] ||= []) << item
93
+ end
94
+ grouped
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,5 @@
1
+ module Jekyll
2
+ module WebmentionsStatic
3
+ VERSION = "0.1.1"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ require "jekyll-webmentions-static/version"
2
+ require "jekyll-webmentions-static/fetcher"
3
+ require "jekyll/webmentions_generator"
4
+ require "jekyll/webmentions_tag"
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jekyll-webmentions-static
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Jason Chance
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-08-21 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: jekyll
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '4.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '4.0'
26
+ description: |
27
+ jekyll-webmentions-static fetches Webmention.io mentions during Jekyll build
28
+ and injects them as static data into site.data["webmentions"] keyed by post URL.
29
+ No JavaScript, no runtime API calls — pure static HTML.
30
+ email:
31
+ - jason@jasonchance.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - LICENSE.txt
37
+ - README.md
38
+ - lib/jekyll-webmentions-static.rb
39
+ - lib/jekyll-webmentions-static/fetcher.rb
40
+ - lib/jekyll-webmentions-static/version.rb
41
+ - lib/jekyll/webmentions_generator.rb
42
+ - lib/jekyll/webmentions_tag.rb
43
+ homepage: https://github.com/jchance/jekyll-webmentions-static
44
+ licenses:
45
+ - MIT
46
+ metadata:
47
+ source_code_uri: https://github.com/jchance/jekyll-webmentions-static
48
+ changelog_uri: https://github.com/jchance/jekyll-webmentions-static/blob/main/CHANGELOG.md
49
+ bug_tracker_uri: https://github.com/jchance/jekyll-webmentions-static/issues
50
+ rubygems_mfa_required: 'true'
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '2.7'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.6.2
66
+ specification_version: 4
67
+ summary: Fetch and render Webmention.io mentions as static HTML at Jekyll build time.
68
+ test_files: []