From 3d615f7b4c19abf34f23ff09ea587a597f2de42a Mon Sep 17 00:00:00 2001 From: Kumar Damani Date: Fri, 24 Jul 2026 00:29:08 -0400 Subject: converted to typst --- global/common.typ | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 global/common.typ (limited to 'global/common.typ') diff --git a/global/common.typ b/global/common.typ new file mode 100644 index 0000000..14738a6 --- /dev/null +++ b/global/common.typ @@ -0,0 +1,228 @@ +// Site template for kumardamani.net, adapted from the example Typst blog. +// Structured for Typst's bundle export: pages are emitted as #document +// elements from the main.typ entrypoint and cross-page links use labels. + +#let _site-info = state("site-info", none) +#let _page-name = state("page-name", none) +#let _page-dir = state("page-dir", "") + +// Image paths collected from pages, emitted as bundle assets in main.typ. +#let _asset-paths = state("asset-paths", ()) + +#let srcurl = "https://gitlab.com/kdam0/kumardamani.net" + +// Deployment info, overwritten by CI per build: +// printf '{ "date": "%s", "link": "%s" }\n' \ +// "$(date '+%B %d, %Y' | sed 's/ 0/ /')" "$CI_JOB_URL" > ci.json +// date is pre-formatted for display, e.g. "July 24, 2026". +#let ci = json("/ci.json") + +// Page ids ("post/self-host") map to labels on the corresponding #document +// elements ("post-self-host"); typst resolves relative links between +// documents automatically. +#let label-for(id) = label(id.replace("/", "-")) + +#let extlink( + dest, + attrs: (:), + body +) = html.elem( + "a", + attrs: ( + class: "extlink", + rel: "external", + target: "_blank", + href: dest, + ) + attrs, + body +) + +#let navbar(pages, current) = { + html.elem("nav", html.elem("ul", { + for (pagename, prefs) in pages { + let (title,) = prefs + let hidden = prefs.at("hidden", default: false) + + if hidden and pagename != current { + continue + } + + let attrs = (:) + let class = () + + if pagename == current { + class += ("current",) + } + + if hidden { + class += ("hidden",) + } + + if class != () { + attrs += ("class": class.join(" ")) + } + + if pagename == current { + // the current page's nav item is not a link + html.elem("li", attrs: attrs, title) + } else if "url" in prefs { + html.elem("li", attrs: attrs, extlink(prefs.url, title)) + } else { + html.elem("li", attrs: attrs, link(label-for(pagename), title)) + } + } + })) +} + +// Image helpers. These emit plain tags with literal src paths so that +// images stay as separate files instead of being embedded into the HTML, and +// register local sources as bundle assets (emitted in main.typ). +#let img(src, alt: "", width: none) = { + if not src.starts-with("http") { + context { + let dir = _page-dir.get() + _asset-paths.update(s => s + (dir + src,)) + } + } + + let attrs = (src: src, alt: alt) + if width != none { + attrs.insert("style", "width: " + width) + } + html.elem("img", attrs: attrs) +} + +#let fig(src, alt: "", caption: none, width: none) = html.elem("figure", { + img(src, alt: alt, width: width) + if caption != none { + html.elem("figcaption", caption) + } +}) + +#let _preferred-title(id) = { + let page = _site-info.get().at("pages").at(id) + + if "long-title" in page { + return page.at("long-title") + } else { + return page.at("title") + } +} + +// The preferred (long, if available) title of the current page. +#let preferred-title = context { + _preferred-title(_page-name.get()) +} + +// The publication date of the current page, if any. +#let page-date = context { + let info = _site-info.get().at("pages").at(_page-name.get()) + if "date" in info { + html.elem("p", attrs: (class: "date"), + html.elem("time", attrs: (datetime: info.date), info.date)) + } +} + +// Link to another page on the site; relative paths are resolved by typst. +#let pagelink(id, body) = link(label-for(id), body) + +// Link to another page, using its preferred title as the link text. +#let pageref(id) = context { + link(label-for(id), _preferred-title(id)) +} + +#let page( + pagename, + site-info, + banner: none, + stylesheets: (), + body +) = { + let site = site-info.at("title", default: "Untitled") + let pages = site-info.at("pages", default: (:)) + + if banner == none { + banner = html.elem("h1", attrs: (class: "banner"), site) + } + + // Prefix for links back to the site root from nested pages (posts). + let parts = pagename.split("/") + let depth = parts.len() - 1 + let base = "../" * depth + + html.html( + lang: "en", + { + html.head({ + html.meta(charset: "utf-8") + html.meta(name: "viewport", content: "width=device-width, initial-scale=1") + html.title(pages.at(pagename).title + " – " + site) + + html.elem("link", attrs: ("rel": "vcs-git", "title": "site source", "href": srcurl)) + html.link(rel: "icon", href: base + "favicon.svg", type: "image/svg+xml") + 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") + + for ss in stylesheets { + html.link(rel: "stylesheet", href: base + ss) + } + }) + html.body({ + _site-info.update(site-info) + _page-name.update(pagename) + _page-dir.update(if depth > 0 { parts.slice(0, -1).join("/") + "/" } else { "" }) + + show html.elem: it => { + if "title" in it.attrs { + let attrs = it.attrs + let has-class = "class" in it.attrs + + if not has-class or regex("\btooltip\b") not in it.attrs.class { + if has-class { + attrs.class += " tooltip" + } else { + attrs.class = "tooltip" + } + + it = html.elem(it.tag, attrs: attrs, it.body) + } + } + + it + } + + show outline.entry: it => { + if it.prefix() != none { + html.span(class: "prefix", it.prefix()) + } + link(it.element.location(), it.body()) + } + + html.header({ + banner + navbar(pages, pagename) + }) + + html.main(body) + html.hr() + html.footer({ + html.span(class: "updated")[Updated: #ci.date.] + html.span(class: "logs")[#extlink(ci.link)[Logs] for nerds!] + }) + }) + } + ) +} + +// Scaffold for post pages: numbered headings (the heading counter is global +// to the bundle, so it is reset per document) with title and date on top. +#let post-page(pagename, site-info, body) = page(pagename, site-info, { + set heading(numbering: "1.") + counter(heading).update(0) + + title(preferred-title) + page-date + + body +}) -- cgit v1.2.3