diff --git a/_includes/layouts/base.njk b/_includes/layouts/base.njk
index 073b096..24780bd 100644
--- a/_includes/layouts/base.njk
+++ b/_includes/layouts/base.njk
@@ -96,6 +96,10 @@
{% if site.markdownAgents.enabled and page.url and page.url.startsWith('/articles/') and page.url != '/articles/' %}
{% endif %}
+ {% if category and page.url and page.url.startsWith('/categories/') and page.url != '/categories/' %}
+
+
+ {% endif %}
diff --git a/category-feed.njk b/category-feed.njk
new file mode 100644
index 0000000..0ada9b6
--- /dev/null
+++ b/category-feed.njk
@@ -0,0 +1,47 @@
+---
+eleventyExcludeFromCollections: true
+eleventyImport:
+ collections:
+ - categoryFeeds
+pagination:
+ data: collections.categoryFeeds
+ size: 1
+ alias: categoryFeed
+permalink: "categories/{{ categoryFeed.slug }}/feed.xml"
+---
+
+
+
+ {{ site.name }} — {{ categoryFeed.name }}
+ {{ site.url }}/categories/{{ categoryFeed.slug }}/
+ Posts tagged with "{{ categoryFeed.name }}" on {{ site.name }}
+ {{ site.locale | default('en') }}
+
+
+ {{ categoryFeed.posts | getNewestCollectionItemDate | dateToRfc822 }}
+ {%- for post in categoryFeed.posts %}
+ {%- set absolutePostUrl = site.url + post.url %}
+ {%- set postImage = post.data.photo %}
+ {%- if postImage %}
+ {%- if postImage[0] and (postImage[0] | length) > 10 %}
+ {%- set postImage = postImage[0] %}
+ {%- endif %}
+ {%- endif %}
+ {%- if not postImage or postImage == "" %}
+ {%- set postImage = post.data.image or (post.content | extractFirstImage) %}
+ {%- endif %}
+ -
+ {{ post.data.title | default(post.content | striptags | truncate(80)) | escape }}
+ {{ absolutePostUrl }}
+ {{ absolutePostUrl }}
+ {{ post.date | dateToRfc822 }}
+ {{ post.content | htmlToAbsoluteUrls(absolutePostUrl) | escape }}
+ {%- if postImage and postImage != "" and (postImage | length) > 10 %}
+ {%- set imageUrl = postImage | url | absoluteUrl(site.url) %}
+
+
+ {%- endif %}
+
+ {%- endfor %}
+
+
diff --git a/eleventy.config.js b/eleventy.config.js
index 23665e3..48a55e4 100644
--- a/eleventy.config.js
+++ b/eleventy.config.js
@@ -908,6 +908,22 @@ export default function (eleventyConfig) {
`${siteUrl}/feed.xml`,
`${siteUrl}/feed.json`,
];
+
+ // Discover category feed URLs from build output
+ const outputDir = directories?.output || dir.output;
+ const categoriesDir = resolve(outputDir, "categories");
+ try {
+ for (const entry of readdirSync(categoriesDir, { withFileTypes: true })) {
+ if (entry.isDirectory() && existsSync(resolve(categoriesDir, entry.name, "feed.xml"))) {
+ feedUrls.push(`${siteUrl}/categories/${entry.name}/feed.xml`);
+ feedUrls.push(`${siteUrl}/categories/${entry.name}/feed.json`);
+ }
+ }
+ } catch {
+ // categoriesDir may not exist on first build — ignore
+ }
+
+ console.log(`[websub] Notifying hub for ${feedUrls.length} URLs...`);
for (const feedUrl of feedUrls) {
try {
const res = await fetch(hubUrl, {