summaryrefslogtreecommitdiff
path: root/docs/site/site.typ
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 11:11:25 -0300
committerGabriel Schneider <[email protected]>2026-10-01 12:24:31 -0300
commit5874742494a293c5e285d2f1284c748e39d980f8 (patch)
treee3fa445947f90e2e6262b3466430b901d2e62295 /docs/site/site.typ
parentd3638abbb3d177ea08beed3b09cd9682b3732daf (diff)
downloadpardes-5874742494a293c5e285d2f1284c748e39d980f8.tar.gz
pardes-5874742494a293c5e285d2f1284c748e39d980f8.zip
The docs become a website: a page per chapter, a landing page with recordings, one Typst bundle run
docs/site/site.typ writes every page in one bundle compile: the landing page (the tagline, the recordings listed in media/clips.toml, links to start from), a page per entry of `chapters` in style.typ, and the tutor. Every page shares a tag-line header with the chapter words; #doc links resolve to the right page and anchor; a chapter with four or more sections gets an "On this page" list. Links are relative and pages flat, so it reads from file:// or any static server. `zig build site` installs it to <prefix>/share/doc/pardes/site and fails plainly without typst. The look is 0x4200.cafe's lapis layout (framed panels, hard shadows, a tag-line trail, Departure Mono headings over Crimson Pro) in orchard's greens and amber, forestbones_light when the reader prefers light; both fonts are OFL and shipped. In HTML a key is one pill with its + and ›, and a mouse button is an inline SVG in currentColor. The tagline lives once in style.typ for the sheet, the book and the site, and the single page html.typ gives way to the site. The landing leads with one plain sentence (columns of panes, click to run or open, helix keys, scripts through files), then the tagline and a line on where it runs; a hero image is the first ready clip's poster; "Start: the guide" leads the links, "Hands on: the tutor" next, the rest a plain row; the captions use no undefined words. docs/typ/install.typ holds the install lines once: setup.typ has an Install section that includes it, and the landing includes it too. Building says up front it is for contributors and points at setup to install. In HTML the guide's glossary is a closed <details> (#glossary, the same heading and table on paper), each drawn mouse carries hidden text (left-click, hold left, click middle) for screen readers, w3m and copying, and the footer takes a source link and a licence from `project` in style.typ once decided. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'docs/site/site.typ')
-rw-r--r--docs/site/site.typ161
1 files changed, 161 insertions, 0 deletions
diff --git a/docs/site/site.typ b/docs/site/site.typ
new file mode 100644
index 00000000..03d9a15d
--- /dev/null
+++ b/docs/site/site.typ
@@ -0,0 +1,161 @@
+// The website: one Typst bundle run writes every page, the stylesheet, the
+// fonts and the media. Pages come from `chapters` in ../typ/style.typ, so a
+// new chapter is a new page; the only prose of its own is the landing page.
+// typst compile --root . --format bundle --features bundle,html docs/site/site.typ OUT
+// Every link is relative and every page sits at the top of OUT, so the site
+// works from file:// and from any static server.
+#import "../typ/style.typ": chapters, tutor-path, doc-links, tagline, project
+
+#let clips = toml("media/clips.toml").clip
+#let pages = (
+ (id: "index", title: "pardes"),
+ ..chapters.filter(c => c.file != none).map(c => (id: c.id, title: c.title, file: c.file)),
+ (id: "tutor", title: "The tutor"),
+)
+#let href(id) = id + ".html"
+
+// --- the files that are not pages --------------------------------------------
+
+#asset("site.css", read("site.css", encoding: none))
+#for f in ("DepartureMono-Regular.woff2", "CrimsonPro-VF-normal.woff2", "CrimsonPro-VF-italic.woff2",
+ "LICENSE-DepartureMono.txt", "LICENSE-CrimsonPro.txt") {
+ asset("fonts/" + f, read("fonts/" + f, encoding: none))
+}
+#asset("media/placeholder.svg", read("media/placeholder.svg", encoding: none))
+#for c in clips.filter(c => c.ready) {
+ for ext in ("webm", "mp4", "png") {
+ asset("media/" + c.name + "." + ext, read("media/" + c.name + "." + ext, encoding: none))
+ }
+}
+
+// --- the shell every page sits in ---------------------------------------------
+
+/// An HTML element: el("a", (href: "x.html"))[text], or el("li")[text].
+#let el(tag, ..args) = {
+ let p = args.pos()
+ html.elem(tag, attrs: if p.len() > 1 { p.first() } else { (:) }, p.last())
+}
+
+/// The running head: a tagline, the way a pane's tag reads, with the path
+/// of this page and the chapters as its words.
+#let trail(id, title) = el("header", (class: "trail"))[
+ #el("span", (class: "grip", "aria-hidden": "true"))[]
+ #el("a", (class: "home", href: href("index")))[pardes]
+ #if id != "index" {
+ el("span", (class: "sep", "aria-hidden": "true"))[/]
+ el("span", (class: "here", "aria-current": "page"))[#title]
+ }
+ #el("nav", (class: "words", "aria-label": "Chapters"))[
+ #for p in pages.slice(1) {
+ if p.id == id { el("a", (href: href(p.id), "aria-current": "page"))[#p.title] }
+ else { el("a", (href: href(p.id)))[#p.title] }
+ }
+ ]
+]
+
+#let shell(id, title, body) = html.html(lang: "en", {
+ html.head({
+ html.meta(charset: "utf-8")
+ el("meta", (name: "viewport", content: "width=device-width, initial-scale=1"))[]
+ el("meta", (name: "color-scheme", content: "dark light"))[]
+ html.title(if id == "index" { "pardes" } else { title + " · pardes" })
+ el("link", (rel: "stylesheet", href: "site.css"))[]
+ })
+ html.body({
+ el("div", (class: "shell"))[
+ #trail(id, title)
+ #el("main", (class: "panel", "data-page": id))[
+ #el("article", (class: "content"))[#body]
+ ]
+ #el("footer", (class: "colophon"))[
+ pardes ·
+ #if project.source != none [#el("a", (href: project.source))[source] · ]
+ #if project.license != none [#project.license · ]
+ built from `docs/typ` with Typst ·
+ #el("a", (href: "fonts/LICENSE-DepartureMono.txt"))[fonts] (OFL)
+ ]
+ ]
+ })
+})
+
+/// The headings of this page only, as a contents list, when there are
+/// enough of them to need one.
+#let toc(id) = context {
+ let hs = query(selector(heading.where(level: 2)).after(label("start-" + id)).before(label("end-" + id)))
+ if hs.len() >= 4 {
+ el("nav", (class: "toc", "aria-label": "On this page"))[
+ #el("div", (class: "toc-bar"))[#el("span")[On this page]]
+ #el("ol")[#for h in hs { el("li")[#link(h.location(), h.body)] }]
+ ]
+ }
+}
+
+// --- the pages ------------------------------------------------------------------
+
+#doc-links.update(true)
+
+#let ready = clips.filter(c => c.ready)
+#let start = (
+ (id: "guide", label: [Start: the guide], note: [about 15 min]),
+ (id: "tutor", label: [Hands on: the tutor], note: none),
+)
+
+#document(href("index"), title: "pardes", shell("index", "pardes")[
+ #el("h1", (class: "masthead"))[pardes]
+ #el("p", (class: "lead"))[pardes is an editor and terminal in columns of panes, each
+ a file, a terminal, a PDF or an image. Click any text to run it or open
+ it; the keys are helix's; scripts drive it through files.]
+ #el("p", (class: "pitch"))[#tagline.]
+ #el("p", (class: "runs"))[It runs in a terminal or in its own window (SDL), on Linux;
+ macOS and browser builds are in the tree.]
+ #if ready.len() > 0 {
+ el("img", (class: "hero", src: "media/" + ready.first().name + ".png", alt: ready.first().caption))[]
+ }
+ #el("div", (class: "start"))[
+ #for s in start {
+ el("a", (class: "go", href: href(s.id)))[#s.label#if s.note != none [ #el("span")[(#s.note)]]]
+ }
+ ]
+ #el("p", (class: "rest"))[
+ #for p in pages.slice(1).filter(p => p.id not in start.map(s => s.id)) {
+ el("a", (href: href(p.id)))[#p.title]
+ }
+ ]
+ #el("div", (class: "clips"))[
+ #for c in clips {
+ el("figure", (class: "clip"))[
+ #if c.ready {
+ el("video", (poster: "media/" + c.name + ".png", muted: "", loop: "", playsinline: "",
+ controls: "", preload: "none"))[
+ #el("source", (src: "media/" + c.name + ".webm", type: "video/webm"))[]
+ #el("source", (src: "media/" + c.name + ".mp4", type: "video/mp4"))[]
+ ]
+ } else {
+ el("img", (src: "media/placeholder.svg", alt: "", width: "640", height: "400"))[]
+ }
+ #el("figcaption")[#c.caption]
+ ]
+ }
+ ]
+ #el("h2", (class: "install"))[Install]
+ #include "../typ/install.typ"
+])
+
+#for p in pages.slice(1, -1) {
+ document(href(p.id), title: p.title, shell(p.id, p.title)[
+ #metadata(none) #label("start-" + p.id)
+ #heading(level: 1, p.title) #label("ch-" + p.id)
+ #toc(p.id)
+ #{
+ set heading(offset: 1)
+ include "../typ/" + p.file
+ }
+ #metadata(none) #label("end-" + p.id)
+ ])
+}
+
+#document(href("tutor"), title: "The tutor", shell("tutor", "The tutor")[
+ #heading(level: 1)[The tutor] <tutor>
+ The tutor as the editor shows it (#raw("Tutor"), or #raw("SPC h t")).
+ #el("pre", (class: "tutor"))[#read(tutor-path)]
+])