diff --git a/quartz/plugins/emitters/contentIndex.tsx b/quartz/plugins/emitters/contentIndex.tsx
index 56392b358..e00a87f58 100644
--- a/quartz/plugins/emitters/contentIndex.tsx
+++ b/quartz/plugins/emitters/contentIndex.tsx
@@ -2,7 +2,7 @@ import { Root } from "hast"
import { GlobalConfiguration } from "../../cfg"
import { getDate } from "../../components/Date"
import { escapeHTML } from "../../util/escape"
-import { FilePath, FullSlug, SimpleSlug, joinSegments, simplifySlug } from "../../util/path"
+import { FilePath, FullSlug, SimpleSlug, getAllSegmentPrefixes, joinSegments, simplifySlug } from "../../util/path"
import { QuartzEmitterPlugin } from "../types"
import { toHtml } from "hast-util-to-html"
import { write } from "./helpers"
@@ -28,6 +28,8 @@ interface Options {
rssFullHtml: boolean
rssSlug: string
includeEmptyFiles: boolean
+ includeTags: boolean
+ rssTagsLimit: number
}
const defaultOptions: Options = {
@@ -37,6 +39,8 @@ const defaultOptions: Options = {
rssFullHtml: false,
rssSlug: "index",
includeEmptyFiles: true,
+ includeTags: false,
+ rssTagsLimit: 15,
}
function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndexMap): string {
@@ -84,8 +88,8 @@ function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndexMap, limit?:
${escapeHTML(cfg.pageTitle)}
https://${base}
${!!limit ? i18n(cfg.locale).pages.rss.lastFewNotes({ count: limit }) : i18n(cfg.locale).pages.rss.recentNotes} on ${escapeHTML(
- cfg.pageTitle,
- )}
+ cfg.pageTitle,
+ )}
Quartz -- quartz.jzhao.xyz
${items}
@@ -135,6 +139,39 @@ export const ContentIndex: QuartzEmitterPlugin> = (opts) => {
slug: (opts?.rssSlug ?? "index") as FullSlug,
ext: ".xml",
})
+
+ if (opts?.includeTags && (opts.rssTagsLimit ?? 0) > 0) {
+ const tagCounts: Map = new Map()
+
+ // Count tags from all non-empty files (unless includeEmptyFiles is true)
+ for (const [_, content] of linkIndex) {
+ const tags = content.tags.flatMap(getAllSegmentPrefixes)
+ for (const tag of new Set(tags)) { // Use Set to avoid double counting per file
+ tagCounts.set(tag, (tagCounts.get(tag) ?? 0) + 1)
+ }
+ }
+
+ const sortedTags = Array.from(tagCounts.entries())
+ .sort((a, b) => b[1] - a[1]) // Sort by frequency descending
+ .slice(0, opts.rssTagsLimit)
+ .map(([tag]) => tag)
+
+ for (const tag of sortedTags) {
+ const tagFilteredIndex = new Map(
+ Array.from(linkIndex).filter(([_, content]) => {
+ const fileTags = new Set(content.tags.flatMap(getAllSegmentPrefixes))
+ return fileTags.has(tag)
+ })
+ )
+
+ yield write({
+ ctx,
+ content: generateRSSFeed(cfg, tagFilteredIndex, opts.rssLimit),
+ slug: joinSegments("tags", tag, "index") as FullSlug,
+ ext: ".xml",
+ })
+ }
+ }
}
const fp = joinSegments("static", "contentIndex") as FullSlug