Presentate is a Typst package for creating presentation. It provides you a variety of helper functions to create dynamic slides.
Presentate's helper functions are implemented without using any context
, counter
s, or state
s, so it can be used with any functions in Typst, but with the correct hiding functions though.
Let's start with pause
#import "path-to-presentate-package": *
// unpack and configure the slides.
#let (presentate-slide,) = presentate-config()
#set text(size: 25pt)
#set page(paper: "presentation-16-9")
#presentate-slide(steps: 3, self => [
= Hello Typst!
#set align(horizon)
This is the first `presentate` presentation!
#pause(self, self => [
You can use `pause` to make the content appear after.
#pause(self, self => [
However, the `pause`s must be nested to take effect.
])
])
])
You have to specify the number of repetitions of the slide in steps
argument. This is because I cannot think of a way to access the repetitions without using state
or counter
(Those always cause the limitation of using the helper functions since it only returns content
).
presentate-config
is used to specify the modes such ashandout
anddrafted
.
More examples with pinit
#import "@preview/pinit:0.2.2": *
#presentate-slide(steps: 3, self => [
= Works well with `pinit`
Pythagorean theorem:
$ #pin(1)a^2#pin(2) + #pin(3)b^2#pin(4) = #pin(5)c^2#pin(6) $
#pause(self, self =>[
$a^2$ and $b^2$ : squares of triangle legs
#only(self, 2, {
pinit-highlight(1,2)
pinit-highlight(3,4)
})
#pause(self, self => [
$c^2$ : square of hypotenuse
#pinit-highlight(5,6, fill: green.transparentize(80%))
#pinit-point-from(6)[larger than $a^2$ and $b^2$]
])
])
])
This is imported from minideck package. As you can see, the only
function also have the self
argument. The self
is required to access the current self.subslide
. only
is used for revealing the content at the specific subslides.
Lets introduce you the one-by-one
.
#presentate-slide(steps: 4, self => [
= Lists and Enum
To fully cover the `list` and `enum`, you can modify the `hider` argument in *all* of the helper functions!
#one-by-one(self, hider: it => hide(block(it)),[
+ First Item
], [
+ Second Item
], [
+ Third Item
])
])
Basically what it does is accepting an array of information, and return the information one by one. As you can see, I modified the hider
argument of the one-by-one
function to fully cover the enumeration list. All helper functions in Presentate can be modified in its hiding method to appropriately "hide" the information. Therefore, cetz
functions can be hidden too, but with some modifications.
#import "@preview/cetz:0.3.1": canvas, draw
#presentate-slide(steps: 3, self =>[
#let cetz-uncover = uncover.with(hider: draw.hide.with(bounds: true))
= In a CeTZ figure
Above canvas
#canvas({
import draw: *
only(self, 3, rect((0,-2), (14,4), stroke: 3pt))
cetz-uncover(self, from: 2, rect((0,-2), (16,2), stroke: blue+3pt))
content((8,0), box(stroke: red+3pt, inset: 1em)[
A typst box #only(self, 2)[on 2nd subslide]
])
})
Below canvas
])
This snippet is imported from, again, Minideck package. I modified the hider
function used by uncover
for hiding the elements in canvas of CeTZ package. This also can be applied to pause
.
#presentate-slide(steps: 7, self => [
= `pause` in CeTZ
#let cetz-pause = pause.with(hider: draw.hide.with(bounds: true))
#canvas({
import draw: *
circle((), fill: red)
cetz-pause(self, self => {
circle((rel: (1, 0)), fill: green)
cetz-pause(self, self => {
circle((rel: (1, 0)), fill: blue)
})
})
})
#{ self.pauses += 2 }
#pause(self, self => [
Or you can use `one-by-one`.
#let cetz-one-by-one = one-by-one.with(hider: draw.hide.with(bounds: true))
#canvas({
import draw: *
cetz-one-by-one(self,
rect((0, 0), (rel: (3, 2)), fill: red),
rect((), (rel: (3, 2)), fill: green),
rect((rel: (0, -2)), (rel: (-3, -2)), fill: blue),
)
})
])
])
This one is a bit long, but it is very simple. I modified the hider
function used by pause
so that it is conpatible with the canvas
of cetz
. Despite our helper functions flexibility, they can only take effect on the content inside its scope. Therefore, I have to update the number of pauses in the self.pauses
variable by #{ self.pauses += 2 }
so that the content outside the cetz-pause
function appears on the correct subslide.
This is the only place where presentate
uses context
. Let's see the example
#set heading(numbering: "1.1")
#presentate-slide(
steps: 4,
self => [
= Hello Typst!
#alter(self, 4, [
This is the first slide in `presentate`
#one-by-one(
self,
[
You can see this time the heading has numbered!
],
[
By default, `page`, `equation` `figure`, `quote`, and `heading` counters are frozen.
],
[
By the way, you can change the layout by `alter` function
],
)],
it => align(horizon, it),
)
],
)
I implemented fake frozen states by get their value at the beginning of the slides, and update them to that value on every subslides.
If you have more custom states or counters, you can add it via frozen-states
argument in the presentate-config()
function.
Let's see more examples on equations:
#set math.equation(numbering: "(1)")
#let my-label(self, x) = if self.subslide == 1 {
label(x)
}
#presentate-slide(
steps: 5,
self => [
== `math.equation` example
#my-label(self, "first")
For example, the following equation:
#one-by-one(
self,
[
$ E = m c^2 $#my-label(self, "eq1")
$ a^2 + b^2 = c^2 $#my-label(self, "eq2")
],
[
It is shown with the same number!
],
[
However, it is still not referencable.
Typst will complain you about the labeled content occuring in the document.
],
[
So I hacked with `self.subslide`:
@eq1 is from Eistein. \
@eq2 is from Pythagoras.
],
)
],
)
This snippet produce the first slide on topic 1.1, and
#presentate-slide(
steps: 2,
self => [
== More equations
#set align(horizon)
$ K E = 1 / 2 m v^2 $
#pause(
self,
self => [
As mentioned in @first, the equation numbering is working!
],
)
],
)
As you can see, the power of function-based implementation allows users to modify anything, even the label so that we can reference through slides!.
presentate-config(handout: false, drafted: false, theme: it => it, ..args)
pause(self, func)
only(self, ..when, from: none, hider: it => none, func)
uncover(self, ..when, from: none, hider: hide, func)
change(self, before, after)
alter(self, ..when, from: none, before, after)
one-by-one(self, when, ..funcs)
Note that:
func
arguments in the helper functions can be any information, but using callbackself => { .. }
gives you an access to the current subslide byself.subslide
variable.from:
argument is used likefrom
argument in theminideck
package.
presentate
uses self.subslide
to determine when the content should be shown and self.pauses
to keep track of number of pauses. Therefore, all helper functions will have a self
argument to access the current self.subslide
.
- A proper documentation.
- more proper examples of how to use the helper functions.
- Some themes.
Thanks @knuesel for the minideck
package that inspires me the syntax and examples.