diff options
| author | Kumar Damani <me@kumardamani.net> | 2026-08-03 16:55:54 +0000 |
|---|---|---|
| committer | Kumar Damani <me@kumardamani.net> | 2026-08-03 16:55:54 +0000 |
| commit | ba451a6b9ba283b4c519283a45aa523662854677 (patch) | |
| tree | 956589583a2f10bd21037d4b60ecda4e4bfe2337 | |
| parent | 7ccfffa85f120026e65d33bbac4b5735b8dd34bf (diff) | |
added rss2 feed
| -rw-r--r-- | feed.typ | 54 | ||||
| -rw-r--r-- | global/common.typ | 7 | ||||
| -rw-r--r-- | main.typ | 2 |
3 files changed, 60 insertions, 3 deletions
diff --git a/feed.typ b/feed.typ new file mode 100644 index 0000000..75639e0 --- /dev/null +++ b/feed.typ @@ -0,0 +1,54 @@ +// RSS 2.0 feed for the blog posts, emitted as feed.xml by main.typ. +// Items are built from the metadata in site-info.pages (newest first). + +#import "/global/common.typ": baseurl + +#let xml-escape(s) = s.replace("&", "&").replace("<", "<").replace(">", ">") + +// ISO date ("2026-07-28") to RFC 822 ("Tue, 28 Jul 2026 00:00:00 GMT"). +#let rfc822(iso) = { + let (y, m, d) = iso.split("-").map(int) + datetime(year: y, month: m, day: d).display( + "[weekday repr:short], [day] [month repr:short] [year] 00:00:00 GMT" + ) +} + +#let feed-xml(site-info) = { + let pages = site-info.pages + let ids = pages.keys().filter(k => k.starts-with("post/")) + + let items = () + for id in ids { + let p = pages.at(id) + let title = if "long-title" in p { p.long-title } else { p.title } + let url = baseurl + "/" + id + ".html" + + let lines = ( + " <item>", + " <title>" + xml-escape(title) + "</title>", + " <link>" + url + "</link>", + " <guid>" + url + "</guid>", + ) + if "date" in p { + lines.push(" <pubDate>" + rfc822(p.date) + "</pubDate>") + } + if "description" in p { + lines.push(" <description>" + xml-escape(p.description) + "</description>") + } + lines.push(" </item>") + items.push(lines.join("\n")) + } + + ( + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", + "<rss version=\"2.0\">", + "<channel>", + " <title>" + xml-escape(site-info.title) + "</title>", + " <link>" + baseurl + "/</link>", + " <description>Posts from " + xml-escape(site-info.title) + " (" + baseurl + ")</description>", + items.join("\n"), + "</channel>", + "</rss>", + "", + ).join("\n") +} diff --git a/global/common.typ b/global/common.typ index f5f5c6d..d1a0aaa 100644 --- a/global/common.typ +++ b/global/common.typ @@ -10,6 +10,7 @@ #let _asset-paths = state("asset-paths", ()) #let srcurl = "https://git.kumardamani.net/kumardamani.net" +#let baseurl = "https://kumardamani.net" // Deployment info, overwritten by CI per build: // printf '{ "date": "%s", "link": "%s" }\n' \ @@ -183,6 +184,7 @@ html.link(rel: "icon", href: base + "favicon.ico") html.elem("link", attrs: (rel: "preload", href: base + "extlink.svg", "as": "image")) html.link(rel: "stylesheet", href: base + "main.css") + html.link(rel: "alternate", type: "application/rss+xml", title: site + " RSS", href: base + "feed.xml") for ss in stylesheets { html.link(rel: "stylesheet", href: base + ss) @@ -227,10 +229,9 @@ html.main(body) html.hr() html.footer({ - html.span(class: "updated")[Updated: #ci.date.] + html.span(class: "updated")[\[Updated: #ci.date.\]] html.span(class: "logs")[ - #extlink(srcurl)[site source] | - #extlink(ci.link)[logs] for nerds! + \[For nerds: #extlink(srcurl)[site source], #extlink(ci.link)[logs], #link(base + "feed.xml")[rss]\] ] }) }) @@ -4,6 +4,7 @@ #import "/global/common.typ": * #import "/site.typ": site-info +#import "/feed.typ": feed-xml #import "/index.typ" as index-page #import "/software.typ" as software-page @@ -74,6 +75,7 @@ #asset("extlink.svg", read("/global/extlink.svg", encoding: none)) #asset("favicon.ico", read("/global/favicon.ico", encoding: none)) #asset("favicon.svg", read("/global/favicon.svg", encoding: none)) +#asset("feed.xml", bytes(feed-xml(site-info))) // image assets collected from pages via the img/fig helpers #context { |
