summaryrefslogtreecommitdiff
path: root/docs/site/site.typ
diff options
context:
space:
mode:
Diffstat (limited to 'docs/site/site.typ')
-rw-r--r--docs/site/site.typ102
1 files changed, 71 insertions, 31 deletions
diff --git a/docs/site/site.typ b/docs/site/site.typ
index 068b5b9c..afcd1041 100644
--- a/docs/site/site.typ
+++ b/docs/site/site.typ
@@ -4,9 +4,34 @@
// 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
+#import "../typ/style.typ": chapters, tutor-path, doc-links, project
#let clips = toml("media/clips.toml").clip
+#let casts = toml("casts/casts.toml").cast
+#let themes = toml("media/themes/themes.toml")
+/// The version, from build.zig.zon: 0.23.0 reads 0.23.
+#let version = {
+ let v = read("/build.zig.zon").match(regex("\\.version\\s*=\\s*\"([^\"]+)\"")).captures.first()
+ if v.ends-with(".0") and v.split(".").len() == 3 { v.slice(0, -2) } else { v }
+}
+
+// --- the landing page's own words: everything a visitor reads first ----------
+// Change the landing here; the rest of the site is the chapters.
+#let landing = (
+ lead: [pardes is a text environment, after acme and helix, where any text
+ is live: right-click looks at it (opens the file at that line, the URL,
+ or finds the word) and middle-click runs it (a builtin or a shell
+ command). Read PDFs and source, edit code and use terminals with it, in
+ a terminal or in its own window.],
+ status: [Linux; macOS in progress. Version #version.],
+ terminal: [In a terminal],
+ window: [In its own window],
+ start: (
+ (id: "guide", label: [Start: the guide], note: [about 15 min]),
+ (id: "tutor", label: [Hands on: the tutor], note: [run `Tutor` inside pardes]),
+ ),
+ install: [Install],
+)
#let pages = (
(id: "index", title: "pardes"),
..chapters.filter(c => c.file != none).map(c => (id: c.id, title: c.title, file: c.file)),
@@ -22,6 +47,12 @@
asset("fonts/" + f, read("fonts/" + f, encoding: none))
}
#asset("media/placeholder.svg", read("media/placeholder.svg", encoding: none))
+#for f in ("asciinema-player.min.js", "asciinema-player.css", "LICENSE-asciinema-player.txt") {
+ asset("player/" + f, read("player/" + f, encoding: none))
+}
+#if themes.at("ready", default: true) {
+ for t in themes.theme { asset("media/themes/" + t.file, read("media/themes/" + t.file, 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))
@@ -68,11 +99,12 @@
#el("article", (class: "content"))[#body]
]
#el("footer", (class: "colophon"))[
- pardes ·
- #if project.source != none [#el("a", (href: project.source))[source] · ]
+ pardes #version ·
+ source: #project.mirrors.map(m => el("a", (href: m.web))[#m.name]).join(", ") ·
#if project.license != none [#project.license license · ]
built from `docs/typ` with Typst ·
- #el("a", (href: "fonts/LICENSE-DepartureMono.txt"))[fonts] (OFL)
+ #el("a", (href: "fonts/LICENSE-DepartureMono.txt"))[fonts] (OFL) ·
+ #el("a", (href: "player/LICENSE-asciinema-player.txt"))[asciinema-player] (Apache-2.0)
]
]
})
@@ -94,54 +126,62 @@
#doc-links.update(true)
-// The first ready clip plays at the top; the grid shows the rest.
-#let hero = clips.find(c => c.ready)
-#let video(c, extra) = el("video", (poster: "media/" + c.name + ".png", muted: "", loop: "", playsinline: "",
- controls: "") + extra)[
+#let video(c) = 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"))[]
]
-#let start = (
- (id: "guide", label: [Start: the guide], note: [about 15 min]),
- (id: "tutor", label: [Hands on: the tutor], note: none),
-)
+#let placeholder = el("img", (src: "media/placeholder.svg", alt: "", width: "640", height: "400"))[]
+
+/// A terminal recording: a box the player fills, its data inline (so it
+/// plays from file://), its caption under it. The first ready one plays.
+#let ready-casts = casts.filter(c => c.ready)
+#let cast(c, i) = el("figure", (class: if i == 0 { "clip cast first" } else { "clip cast" }))[
+ #if c.ready { el("div", (class: "player", id: "cast-" + c.name, "aria-label": c.title))[] } else { placeholder }
+ #el("figcaption")[#el("strong")[#c.title.] #c.caption]
+]
+#let player-script = {
+ let list = ready-casts.enumerate().map(((i, c)) => (
+ id: "cast-" + c.name, data: read("casts/" + c.name + ".cast"),
+ cols: c.cols, rows: c.rows, autoPlay: i == 0,
+ ))
+ // "</" may not appear inside a script element
+ let data = json.encode(list, pretty: false).replace("</", "<\\/")
+ el("script", (src: "player/asciinema-player.min.js"))[]
+ el("script")[#("for (const c of " + data + ") AsciinemaPlayer.create({data: c.data}, document.getElementById(c.id), {cols: c.cols, rows: c.rows, autoPlay: c.autoPlay, loop: true, fit: 'width'});")]
+}
#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 hero != none {
- el("figure", (class: "clip hero"))[
- #video(hero, (autoplay: ""))
- #el("figcaption")[#hero.caption]
- ]
- }
+ #el("p", (class: "lead"))[#landing.lead]
+ #el("p", (class: "runs"))[#landing.status]
#el("div", (class: "start"))[
- #for s in start {
+ #for s in landing.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)) {
+ #for p in pages.slice(1).filter(p => p.id not in landing.start.map(s => s.id)) {
el("a", (href: href(p.id)))[#p.title]
}
]
+ #el("h2", (class: "shelf"))[#landing.terminal]
+ #el("div", (class: "casts"))[#for (i, c) in casts.enumerate() { cast(c, i) }]
+ #el("h2", (class: "shelf"))[#landing.window]
#el("div", (class: "clips"))[
- #for c in clips.filter(c => c != hero) {
+ #for c in clips {
el("figure", (class: "clip"))[
- #if c.ready { video(c, (preload: "none")) } else {
- el("img", (src: "media/placeholder.svg", alt: "", width: "640", height: "400"))[]
- }
+ #if c.ready { video(c) } else { placeholder }
#el("figcaption")[#c.caption]
]
}
]
- #el("h2", (class: "install"))[Install]
+ #el("h2", (class: "install"))[#landing.install]
#include "../typ/install.typ"
+ #if ready-casts.len() > 0 {
+ el("link", (rel: "stylesheet", href: "player/asciinema-player.css"))[]
+ player-script
+ }
])
#for p in pages.slice(1, -1) {