Aus einer Quelle drei Ausgaben#
Aus derselben Datei entstehen die Präsentation für die Leinwand, der Foliensatz zum Nachlesen und das Handout zum Mitschreiben.
Der Foliensatz#
Der PDF-Lauf ergibt ohne weitere Angabe eine Seite je Folie, in der Größe der Leinwand. Jedes Element steht in seinem Endzustand: Was eingeblendet wird, ist da; von mehreren Fassungen an derselben Stelle steht die letzte. Was allein zur Bewegung gehört – Notizen, Übergänge, Aufträge an eingebettete Elemente –, fällt weg.
Das Handout#
Ein einziges Argument macht aus dem Foliensatz ein Handout auf A4:
#show: presentation.with(handout: 3) // drei Folien je Seitehandout nimmt true (zwei je Seite) oder eine Zahl von 1 bis 6 und wirkt nur auf die PDF. Die Folien werden nur verkleinert, nicht neu gesetzt: Ein Handout kann nicht von dem abweichen, was auf der Leinwand stand.
Alle drei Ausgaben in einem Lauf#
bundle schreibt alle drei auf einmal:
#bundle(
theme: themes.lesson,
title: [Completing the Square],
handout: "handout.pdf",
)[
= Ein Abschnitt
== Eine Folie
Text.
]typst compile --features bundle,html --format bundle vortrag.typ ausgabehtml, slides und handout sind Dateinamen, none lässt die jeweilige Ausgabe weg, per-sheet sind die Folien je Handout-Blatt. Alles Übrige geht unverändert an presentation.
Was das Paket nachschlägt, bleibt in der eigenen Ausgabe. Die Zähler fangen je Ausgabe neu an, auch die der Abbildungen, der Gleichungen und die eigenen eines Decks, und ein Verweis wie ein Eintrag von contents() führt in die eigene Datei. Zweierlei führt Typst dagegen über das ganze Bündel, und daran reicht das Paket nicht heran. Ein eigener state läuft von einer Ausgabe in die nächste weiter, weil es keinen Wert gibt, auf den er zurückfallen könnte; eine laufende Nummer gehört deshalb in einen counter. Und eine Marke steht in jeder Ausgabe einmal: Ein Verweis wie @abb bricht das Bündel mit „label occurs multiple times in the document“ ab, auch wenn das Deck allein übersetzt, und ein eigenes outline(target: figure) führt die Abbildungen aller Ausgaben auf.
Mit pages: "step" gibt es eine Grenze mehr. Steht dann ein Aufdecken in einer Fassung von alternatives oder einer Stufe von build und folgt noch eine Folie – #alternatives([A], [#anim[x]]) –, meldet das Bündel, dass es nicht konvergiert, und in talk.html erscheint das x nie. Ein solches Deck baut seine HTML für sich, mit html: none im Bündel.
--features bundle,html nicht zu haben. Und eine Datei, die bundle benutzt, lässt sich nur mit --format bundle übersetzen; ein gewöhnliches typst compile vortrag.typ vortrag.pdf bricht mit „constructing a document is only supported in the bundle target“ ab. Wer beide Wege offenhalten will, legt den Rumpf in ein #let und ruft presentation von Hand.Neben oder unter jeder Folie steht ihre Notiz; wo eine Folie keine hat, treten Schreiblinien an ihre Stelle. Bis zwei Folien je Seite stehen sie darunter, ab drei daneben. Unter einer Folie bleiben mindestens vier Linien: Eine 4:3-Folie, die bei zwei je Seite sonst die ganze Höhe nähme, wird dafür schmaler.
Notizen#
speaker-note legt eine Notiz zur Folie ab. Sie steht im Folienrumpf oder als Angabe note an slide:
== Der Satz des Pythagoras
#speaker-note[
Erst die Zerlegung zeigen, dann die Formel -- nicht umgekehrt.
]Die Notiz erscheint in der Sprecheransicht – dort geht nur der reine Text ein, Auszeichnungen fallen weg – und im Handout bei ihrer Folie.
Eine Notiz muss Text tragen. Eine, die nur aus Layout besteht (ein fit, ein blankes rect, ein Bild), wird mit einer Meldung abgewiesen. Was gesehen werden soll, gehört auf die Folie.
Zwei Uhren für die Klasse#
t startet die Vollbilduhr. Sie deckt die Folie zu, von Rand zu Rand, mit Ziffern für die letzte Reihe: Der Saal macht Pause. Blättern beendet sie.
⇧T startet die angeheftete Uhr. Sie steht auf der Folie und lässt die Aufgabe darunter stehen; Blättern beendet sie nicht. In der Sprecheransicht lässt sie sich mit der Maus anfassen: ein Zug in der Mitte verschiebt sie, ein Zug am Rand zieht sie größer oder kleiner. Der Zeiger sagt vorher, welches von beidem. Ort und Größe wandern im Vortragsfenster mit, als Bruchteile der Bühne – die Uhr steht dort an derselben Stelle der Folie und gleich groß, obwohl das Fenster ein anderes ist.
Beide fragen zuerst nach den Minuten und laufen erst danach. ⇧← und ⇧→ geben eine Minute mehr oder weniger; derselbe Tastendruck noch einmal beendet die Uhr.
class-clock schreibt ins Deck, wie lange die Aufgabe gedacht war:
#slide[
= Gruppenarbeit
#class-clock(12)
Sammelt zu zweit drei Beispiele.
]Gestartet wird dadurch nichts: ⇧T bietet die zwölf Minuten an, die Lehrkraft bestätigt oder ändert sie, und erst dann läuft die Uhr.
Die Ziffern stellen die Uhr#
Eine Frage am Stundenanfang, ein Gespräch zu zweit: die Hand liegt ohnehin auf der Tastatur, und eine Zahl ist kürzer als t, Feld, Zahl, Eingabe. 3 startet drei Minuten, 7 sieben, 0 beendet sie wieder.
Gestartet wird die angeheftete Uhr. Die Frage bleibt damit stehen, während die Zeit läuft, und Blättern beendet sie nicht. Und sie geht ohne zweites Fenster: ein Rechner am Beamer genügt. Die Uhr war bisher nur am Pult erreichbar, und das ist nicht die Anordnung, in der man unterrichtet.
Auf einer Folie mit cue()-Gruppe gehören die Ziffern der Gruppe. Das gilt für die ganze Folie und nicht für den einzelnen Anschlag: auch eine Ziffer, die die Gruppe nicht hat, und der zweite Druck auf denselben Punkt starten dort keine Uhr. Eine Folie gehört entweder den Punkten oder der Uhr, und welche von beiden sagt die Folie selbst.
b verdunkelt den Saal und lässt die angeheftete Uhr stehen: wer während einer Gruppenarbeit verdunkelt, nimmt der Klasse die Ablenkung und nicht ihre Zeit. Die Vollbilduhr weicht weiterhin – sie deckt den Saal ohnehin zu.
Wie ruhig die Uhr liest#
Eine Uhr, die im Sekundentakt springt, zieht den Blick jedes Mal von der Aufgabe weg. room setzt den Schritt für das ganze Deck:
#show: presentation.with(
room: (clock: (step: 5)), // die Zahl springt nur alle fünf Sekunden
)Die letzte Stufe zählt trotzdem einzeln herunter – 00:15, 00:10, 00:05, 00:04, 00:03, 00:02, 00:01, 00:00. Eine Uhr, die volle fünf Sekunden lang 00:00 zeigt, während noch Zeit übrig ist, schickt die Klasse zu früh nach Hause.
Der Schritt muss 60 teilen: 1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30 oder 60 Sekunden. Statt der Zahl geht auch duration(seconds: 5). Was nicht aufgeht, wird schon beim Übersetzen abgelehnt:
#show: presentation.with(room: (clock: (step: 7)))Sonst stimmte die Zahl schon im Augenblick des Starts nicht: eine class-clock(1) stünde bei sieben Sekunden Schritt sofort auf 00:56, und das liest sich wie ein Fehler der Uhr und nicht wie einer der Einstellung.
Am Deck und nicht an der Folie, und das ist Absicht. Wie grob die Uhr liest, ist eine Eigenschaft des Auges und nicht der Aufgabe; eine laufende Uhr, die beim Blättern ihren Rhythmus wechselte, sähe aus wie ein Fehler. Wie lange eine Aufgabe dauern soll, ist dagegen tatsächlich je Folie verschieden – dafür gibt es class-clock.
room: (clock: (digits: false)) gibt die Ziffern wieder frei; wer die Uhr ganz abbestellt, nimmt speaker-view: (clock: false) und verliert die Ziffern mit.
Ein Klang auf einer Taste#
Ein Signal, das die Klasse kennt: „Schluss, aufräumen.„ room bindet eine Taste an eine Tondatei.
#show: presentation.with(
room: (sounds: (a: "airhorn.mp3", g: "gong.mp3")),
)Die Datei reist neben der HTML-Datei, wie jede andere Mediendatei auch; das Paket bringt keinen Klang mit. Die Beispieldecks tour und unterrichten tragen ein Horn, das eigens für sie gerechnet ist, keine Aufnahme: examples/medien/airhorn.mp3, und daneben in PROVENANCE.md der Befehl, der es baut. Gehört wird der Klang im Saal und nur dort: am Pult sitzt die Lehrkraft vor dem Gerät, im Raum steht die Anlage, und zweimal ist der Ton nie zu hören. Gedrückt werden darf die Taste in beiden Fenstern.
Frei sind die Buchstaben a g h i j k p q s u v w y – die übrigen gehören der Laufzeit, und ein belegter wird schon beim Übersetzen abgelehnt, mit der Liste der freien dabei:
#show: presentation.with(room: (sounds: (b: "gong.mp3")))Die gewählten Tasten stehen in der Tastenleiste der Sprecheransicht, denn der übersetzte Hilfetext kann sie nicht kennen.
Fehlt die Datei, meldet die Laufzeit das beim Laden und nicht erst, wenn jemand drückt: bei einem fehlenden Bild sieht man ein leeres Rechteck, bei einem fehlenden Ton nichts.
Der Punkt im Saal#
Der Leuchtpunkt des Zeigers, aus „Der Zeiger„ weiter oben, ist von selbst an und braucht keine Angabe. room stellt ihn für den Fall, dass die Vorgabe zum Deck nicht passt:
#show: presentation.with(
room: (pointer: (color: rgb("#00c853"), size: 4%)),
)size ist ein Anteil der Folienbreite und keine Länge, denn die Folienkopie in der Sprecheransicht und die Leinwand im Saal messen sich in verschieden vielen Bildpunkten – auf diesem Rechner gemessen 622 gegen 1600 –, und der Punkt soll auf der Folie in beiden gleich groß sein. Er muss zwischen 0,8 % und 6 % liegen: darunter sind im Saal die beiden Ringe dünner als ein Bildpunkt, und sie sind es, die den Kontrast tragen – bei 0,8 % einer Bühne von 1600 Bildpunkten misst jeder von ihnen nur noch 0,9 Bildpunkte. Darüber verdeckt der Punkt eine Zeile.
#show: presentation.with(room: (pointer: (size: 12%)))Die Farbe ist frei, und freier, als sie aussieht: um den Kern liegen ein heller und ein dunkler Ring, und welchen Grund der Punkt auch trifft – einer der beiden trennt ihn davon ab. Gemessen kommt auf heller Folie der dunkle Ring auf 10,90 und auf themes.night der helle auf 15,45 – gleich, welche Farbe der Kern hat. Ein Verfahren für beide Zahlen, damit zwei Messungen derselben Sache nicht wie ein Widerspruch aussehen: der Punkt steht in einem Saalfenster von 1600 × 900 auf der Mitte der Bühne und in der Vorgabegröße, das Bildschirmfoto wird unskaliert gelesen, der Grund ist die häufigste Farbe auf dem Kreis mit zweieinhalb Radien um die Mitte, die beiden Ringe sind die häufigsten Farben auf 0,57 und 0,71 Radien, gerechnet nach WCAG 2.1. Die helle Folie ist dabei das Vorgabepapier #fafafa. Ein eigenes Grün liest mit demselben Verfahren 2,14 im Kern und bekommt seine 10,90 trotzdem vom dunklen Ring. Eine schlecht gewählte Farbe kostet also Auffälligkeit, nicht Lesbarkeit. Ohne eigene Farbe nimmt der Punkt den Akzent des Decks.
pointer: false nimmt ihn ganz weg:
#show: presentation.with(room: (pointer: false))Eingebettete Rahmen bleiben bedienbar: sie sind die andere Hälfte des Zeigermodus und hängen nicht am Punkt. Was mit false zurückkommt, ist der alte Hinweis – auf einer Folie ohne Einbettung sagt die Sprecheransicht dann wieder, dass es nichts zu zeigen gibt, denn dann stimmt es wieder.
Lesezeichen#
Das PDF trägt ein Verzeichnis, wie jedes andere Typst-Dokument auch: ein Eintrag je Folie, Abschnitte darüber, die Titelfolie ganz oben. Im Reader ist das die Leiste, mit der man springt, statt zu blättern.
Sichtbar ist davon nichts. Jede Folie legt dafür eine Überschrift, der hide die Tinte und place den Platz nimmt; sie steht nur da, um ein Lesezeichen zu tragen. Nötig ist der Umweg, weil die Überschriften, die das Deck in Folien schneiden, dabei zu Wörterbüchern werden und das Dokument nie erreichen – ohne die stille Überschrift gäbe es nichts, woraus Typst ein Verzeichnis bauen könnte.
Eine Überschrift, die ein Deck in einen Folienrumpf schreibt, bekommt kein eigenes Lesezeichen. Sie bekäme sonst eines neben dem der Folie, und unter pages: "step" je Schrittseite noch eines – derselbe Name mehrfach, auf Seiten, auf denen er nicht steht. Eine Folie ohne Titel bleibt aus dem Verzeichnis heraus; ein leerer Eintrag ist schlechter als keiner.
Was auf dem Papier fehlt – und was man dafür vorsieht#
| Auf der Folie | Auf dem Papier |
|---|---|
anim, stagger, #pause | alles sichtbar, an derselben Stelle und im selben Platz |
alternatives | nur die letzte Fassung, im gemeinsamen Kasten |
morph | der Inhalt der jeweiligen Folie – die Kette wird zur Rechnung |
embed, geogebra | fallback, sonst ein Platzhalter mit label; darunter link |
video | das poster, sonst eine graue Fläche |
flipbook | ein einziges Bild: still oder render(0.0) |
scene | ein einziges Bild: still oder der letzte Halt |
speaker-note | im Handout bei der Folie, im gewöhnlichen Foliensatz nichts |
transition, bridge-job | nichts – sie gehören allein zur Bewegung |
fallback und link gleich beim Schreiben der Folie. Nachträglich muss jede eingebettete Stelle noch einmal aufgesucht werden.Die Adresse eines Schritts#
Im Adressfeld steht, wo der Vortrag gerade ist – nicht nur, welche Folie, sondern welcher Schritt darauf:
// vortrag.html#slide-3 Folie 3, ihr erster Schritt
// vortrag.html#slide-3-2 Folie 3, zweiter Schritt
// vortrag.html#speaker die SprecheransichtDer Link lässt sich kopieren und weitergeben; wer ihn öffnet, steht genau dort. Die Zahlen sind die der Folien, von eins an gezählt, und ein Schritt, den es nicht (mehr) gibt, führt auf den letzten seiner Folie statt ins Leere.
#7). So ein Link hielt nur bis zur nächsten eingefügten Folie: Alles dahinter rückte weiter. Er wird weiter gelesen, damit alte Lesezeichen gelten, aber nicht mehr geschrieben.Die HTML weitergeben#
Zur HTML-Ausgabe gehören zwei Dateien: eine Stilvorlage und die Laufzeitumgebung, die die Bewegung ausführt. Wo sie herkommen, sagt assets:
#show: presentation.with(assets: "inline") // Vorgabe
#show: presentation.with(assets: "split")
#show: presentation.with(assets: (cdn: "https://cdn.example.org/ts/"))| Wert | Wirkung und Anlass |
|---|---|
"inline" | Beide Dateien stehen im HTML. Eine einzige Datei, die sich verschicken, auf einen Stick legen und ohne Netz öffnen lässt. Vorgabe – und für den Unterricht meist die richtige Wahl. |
"split" | Das HTML verweist auf typstage-0.2.0.css und typstage-0.2.0.js daneben. Angebracht, wo mehrere Vorträge in einem Ordner liegen: Der Browser lädt die Laufzeit einmal für alle. |
(cdn: …) | Dieselben Namen unter der angegebenen Adresse. Für eine Website, die viele Vorträge trägt. |
Bei kleinen Decks macht die Laufzeit den größten Teil der Datei aus. Wer viele kurze Vorträge nebeneinander veröffentlicht, spart mit "split" deshalb spürbar: Das erste Deck zahlt sie, jedes weitere im selben Ordner nichts mehr. Die Dateinamen führen die Version mit sich, damit kein Browser einen neuen Vortrag aus einem alten Zwischenspeicher bedient.
Typst legt keine Dateien an: Bei "split" und beim CDN müssen die beiden einmal geschrieben werden. Ihr Inhalt steht in runtime-files, und der Bündel-Export gibt sie im selben Lauf aus:
#import "@preview/typstage:0.2.0": *
#document("vortrag.html", title: "Der Satz des Pythagoras")[
#show: presentation.with(title: [Der Satz des Pythagoras], assets: "split")
== Eine Folie
Text auf der Folie.
]
#for datei in runtime-files {
asset(datei.name, bytes(datei.content))
}typst compile vortrag.typ ausgabe --format bundle --features bundle,htmlIm Ordner ausgabe liegen danach vortrag.html und die beiden Dateien, auf die es verweist.