diff --git a/.github/workflows/mdbook.yml b/.github/workflows/mdbook.yml
index 3840a3b..5b52d8c 100644
--- a/.github/workflows/mdbook.yml
+++ b/.github/workflows/mdbook.yml
@@ -7,7 +7,7 @@ name: Deploy mdBook site to Pages
on:
# Runs on pushes targeting the default branch
push:
- branches: ["main"]
+ branches: ["release"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
diff --git a/README.md b/README.md
index 571f83d..62faa45 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,8 @@
# Polylux
This is a package for creating presentation slides in [Typst](https://typst.app/).
+Read the [book](https://andreaskroepelin.github.io/polylux/book) to learn all
+about it and click [here](https://andreaskroepelin.github.io/polylux/book/changelog.html)
+to see what's new!
If you like it, consider [giving a star on GitHub](https://github.com/andreasKroepelin/polylux)!
@@ -14,8 +17,8 @@ If you like it, consider [giving a star on GitHub](https://github.com/andreasKro
## Quickstart
For the bare-bones, do-it-yourself experience, all you need is:
```typ
-// Get polylux from the official package repository
-#import "@preview/polylux:0.2.0": *
+// Get Polylux from the official package repository
+#import "@preview/polylux:0.3.0": *
// Make the paper dimensions fit for a presentation and the text larger
#set page(paper: "presentation-16-9")
@@ -54,7 +57,7 @@ or you can use one of the provided themes.
The simplest one of them is called `simple` (what a coincidence!).
It is still very unintrusive but gives you some sensible defaults:
```typ
-#import "@preview/polylux:0.2.0": *
+#import "@preview/polylux:0.3.0": *
#import themes.simple: *
@@ -95,9 +98,8 @@ It is still very unintrusive but gives you some sensible defaults:
== Dynamic slide
Did you know that...
- #uncover(2)[
- ...you can see the current section at the top of the slide?
- ]
+ #pause
+ ...you can see the current section at the top of the slide?
]
```
This time, we obtain these PDF pages:
@@ -111,23 +113,30 @@ The book
on how to use (and create your own) themes.
-For dynamic content, polylux also provides [a convenient API for complex
+For dynamic content, Polylux also provides [a convenient API for complex
overlays](https://andreaskroepelin.github.io/polylux/book/dynamic/dynamic.html).
+If you use [pdfpc](https://pdfpc.github.io/) to display your slides, you can rely
+on [Polylux' support for it](https://andreaskroepelin.github.io/polylux/book/external/pdfpc.html)
+and create speaker notes, hide slides, configure the timer and more!
+
Visit the
[book](https://andreaskroepelin.github.io/polylux/book)
for more details or take a look at the
[demo PDF](https://github.com/andreasKroepelin/polylux/releases/latest/download/demo.pdf)
where you can see the features of this template in action.
-**⚠ This package is under active development.
-While I try to make sure that the `main`-branch always is in a usable state,
-there are no compatibility guarantees!**
+**⚠ This package is under active development and there are no backwards
+compatibility guarantees!**
## Acknowledgements
Thank you to...
- [@drupol](https://github.com/drupol) for the `university` theme
- [@Enivex](https://github.com/Enivex) for the `metropolis` theme
- [@MarkBlyth](https://github.com/MarkBlyth) for contributing to the `clean` theme
-- [@fncnt](https://github.com/fncnt) for coming up with the name "polylux"
+- [@ntjess](https://github.com/ntjess) for contributing to the height fitting
+ feature
+- [@JuliusFreudenberger](https://github.com/JuliusFreudenberger) for maintaining
+ the `polylux2pdfpc` AUR package
+- [@fncnt](https://github.com/fncnt) for coming up with the name "Polylux"
- the Typst authors and contributors for this refreshing piece of software
diff --git a/book/src/IMPORT.typ b/book/src/IMPORT.typ
new file mode 100644
index 0000000..f6ddd73
--- /dev/null
+++ b/book/src/IMPORT.typ
@@ -0,0 +1 @@
+#import "@preview/polylux:0.3.0": *
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index e810371..8987306 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -8,8 +8,8 @@
- [General syntax](./dynamic/syntax.md)
- [Complex rules](./dynamic/complex.md)
- [Helper functions](./dynamic/helper.md)
- - [one-by-one and line-by-line](./dynamic/obo-lbl.md)
- [pause](./dynamic/pause.md)
+ - [one-by-one and friends](./dynamic/obo-lbl.md)
- [alternatives](./dynamic/alternatives.md)
- [Cover mode](./dynamic/cover.md)
- [Handout mode](./dynamic/handout.md)
@@ -22,4 +22,11 @@
- [University](./themes/gallery/university.md)
- [Bipartite](./themes/gallery/bipartite.md)
- [Build your own](./themes/your-own.md)
- - [Helpers for theme authors](./themes/helpers.md)
+- [Utilities](./utils/utils.md)
+ - [Side by side](./utils/side-by-side.md)
+ - [Fit to height](./utils/fit-to-height.md)
+ - [Progress](./utils/progress.md)
+ - [Sections](./utils/sections.md)
+- [External tools](./external/external.md)
+ - [pdfpc](./external/pdfpc.md)
+- [Changelog](./changelog.md)
diff --git a/book/src/changelog.md b/book/src/changelog.md
new file mode 100644
index 0000000..4fa0442
--- /dev/null
+++ b/book/src/changelog.md
@@ -0,0 +1,29 @@
+# Changelog
+
+## v0.3.0
+
+- The previously existing module `helpers` was transformed to `utils` and now
+ contains many more useful features.
+- The modules `logic` and `utils` are now directly accesible when importing
+ Polylux (it was a bug that it did not work previously).
+- We finally have an ergonomic `#pause` function that does not expect the user
+ to keep track of some counter themselves.
+- The `#alternatives` function has gained lots of friends that make specific
+ situations a bit more convenient, namely `#alternatives-match`,
+ `alternatives-cases`, and `alternatives-fn`.
+ Also, there is a parameter `repeat-last` for `#alternatives` now.
+- Bullet lists, enumerations, and term lists now have custom functions to display
+ them dynamically: `#list-one-by-one`, `#enum-one-by-one`, and `#terms-one-by-one`.
+- There is a new function `#fit-to-height` that allows you to resize content to
+ a given height (especially make it fill the remaining space on a slide!)
+ Thank you to [@ntjess](https://github.com/ntjess) for the initial implementation!
+- Previously, certain themes allowed you to easily put multiple content elements
+ next to each other.
+ This is now a commonly available function: `#side-by-side`.
+ You can use it regardless of any theme and the functionality was removed from
+ the previously implementing themes.
+- Polylux now has special support for the pdfpc presentation viewer.
+ You can add speaker notes, hide slides, configure the timer, and more all from
+ within your Typst source file.
+ Thank you to [@JuliusFreudenberger](https://github.com/JuliusFreudenberger)
+ for the inspiration and for creating the `polylux2pdfpc` AUR package.
diff --git a/book/src/diy/diy.md b/book/src/diy/diy.md
index a7545ab..68ae2f5 100644
--- a/book/src/diy/diy.md
+++ b/book/src/diy/diy.md
@@ -83,7 +83,7 @@ As a quick example, let's add a little quiz to our slides:
What is the capital of the Republic of Benin?
- #uncover(2)[Cotonou]
+ #uncover(2)[Porto-Novo]
]
```
![quiz](quiz.png)
@@ -94,7 +94,7 @@ The next sections will explain dynamic content in polylux in all its details.
For reference, here is the full source code for the slides we developed in this
section:
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../IMPORT.typ}}
#set page(paper: "presentation-16-9", fill: teal.lighten(90%))
#set text(size: 25pt, font: "Blogger Sans")
@@ -118,7 +118,7 @@ section:
What is the capital of the Republic of Benin?
- #uncover(2)[Cotonou]
+ #uncover(2)[Porto-Novo]
]
```
diff --git a/book/src/diy/hello-world.png b/book/src/diy/hello-world.png
index 687ec12..933b0ce 100644
Binary files a/book/src/diy/hello-world.png and b/book/src/diy/hello-world.png differ
diff --git a/book/src/diy/quiz.png b/book/src/diy/quiz.png
index 579c730..f6e8799 100644
Binary files a/book/src/diy/quiz.png and b/book/src/diy/quiz.png differ
diff --git a/book/src/diy/quiz.typ b/book/src/diy/quiz.typ
index 7480042..0d20b3b 100644
--- a/book/src/diy/quiz.typ
+++ b/book/src/diy/quiz.typ
@@ -22,5 +22,5 @@
What is the capital of the Republic of Benin?
- #uncover(2)[Cotonou]
+ #uncover(2)[Porto-Novo]
]
diff --git a/book/src/diy/slide-title.png b/book/src/diy/slide-title.png
index bbd8af4..34d7459 100644
Binary files a/book/src/diy/slide-title.png and b/book/src/diy/slide-title.png differ
diff --git a/book/src/diy/title-slide.png b/book/src/diy/title-slide.png
index 031486a..ea11c3d 100644
Binary files a/book/src/diy/title-slide.png and b/book/src/diy/title-slide.png differ
diff --git a/book/src/dynamic/alternatives-cases.png b/book/src/dynamic/alternatives-cases.png
new file mode 100644
index 0000000..823b763
Binary files /dev/null and b/book/src/dynamic/alternatives-cases.png differ
diff --git a/book/src/dynamic/alternatives-cases.typ b/book/src/dynamic/alternatives-cases.typ
new file mode 100644
index 0000000..a30233a
--- /dev/null
+++ b/book/src/dynamic/alternatives-cases.typ
@@ -0,0 +1,22 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 50pt)
+
+/*
+#alternatives-match((
+ "1, 3" : [
+ Some text
+ ],
+ "2" : [
+ #set text(fill: teal)
+ Some text
+ ],
+))
+*/
+
+#polylux-slide[
+#alternatives-cases(("1, 3", "2"), case => [
+ #set text(fill: teal) if case == 1
+ Some text
+])
+]
diff --git a/book/src/dynamic/alternatives-fn.png b/book/src/dynamic/alternatives-fn.png
new file mode 100644
index 0000000..7252717
Binary files /dev/null and b/book/src/dynamic/alternatives-fn.png differ
diff --git a/book/src/dynamic/alternatives-fn.typ b/book/src/dynamic/alternatives-fn.typ
new file mode 100644
index 0000000..de6cfc8
--- /dev/null
+++ b/book/src/dynamic/alternatives-fn.typ
@@ -0,0 +1,9 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 60pt)
+
+#polylux-slide[
+#alternatives-fn(start: 2, count: 7, subslide => {
+ numbering("(i)", subslide)
+})
+]
diff --git a/book/src/dynamic/alternatives-match.png b/book/src/dynamic/alternatives-match.png
new file mode 100644
index 0000000..ddb491a
Binary files /dev/null and b/book/src/dynamic/alternatives-match.png differ
diff --git a/book/src/dynamic/alternatives-match.typ b/book/src/dynamic/alternatives-match.typ
new file mode 100644
index 0000000..53d2c40
--- /dev/null
+++ b/book/src/dynamic/alternatives-match.typ
@@ -0,0 +1,10 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 50pt)
+
+#polylux-slide[
+#alternatives-match((
+ "1, 3-5": [this text has the majority],
+ "2, 6": [this is shown less often]
+))
+]
diff --git a/book/src/dynamic/alternatives-position.png b/book/src/dynamic/alternatives-position.png
index ffeae1f..6682688 100644
Binary files a/book/src/dynamic/alternatives-position.png and b/book/src/dynamic/alternatives-position.png differ
diff --git a/book/src/dynamic/alternatives-repeat-last.png b/book/src/dynamic/alternatives-repeat-last.png
new file mode 100644
index 0000000..25a56e2
Binary files /dev/null and b/book/src/dynamic/alternatives-repeat-last.png differ
diff --git a/book/src/dynamic/alternatives-repeat-last.typ b/book/src/dynamic/alternatives-repeat-last.typ
new file mode 100644
index 0000000..177c356
--- /dev/null
+++ b/book/src/dynamic/alternatives-repeat-last.typ
@@ -0,0 +1,9 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 50pt)
+
+#polylux-slide[
+#alternatives(repeat-last: true)[temporary][transitory][ephemeral][permanent!]
+
+#uncover(5)[Did I miss something?]
+]
diff --git a/book/src/dynamic/alternatives.md b/book/src/dynamic/alternatives.md
index 6292ada..3801d0d 100644
--- a/book/src/dynamic/alternatives.md
+++ b/book/src/dynamic/alternatives.md
@@ -1,6 +1,5 @@
# `#alternatives` to substitute content
-The so far discussed helpers `#one-by-one`, `#line-by-line`, and `pause` all
-build upon `#uncover`.
+The so far discussed helpers `#pause`, `#one-by-one` etc. all build upon `#uncover`.
There is an analogon to `#one-by-one` that is based on `#only`, namely
`#alternatives`.
You can use it to show some content on one subslide, then substitute it by
@@ -36,6 +35,19 @@ it uses the same amount of space as `Christopher`.
In a sense, it is like a mix of `#only` and `#uncover` with some reserving of
space.
+### Repeat last content
+In case you have other dynamic content on a slide that happens after the contents
+of `#alternatives` are exhausted, you might want to not have the `#alternatives`
+element disappear but instead continue to show its last content argument.
+To achieve this, you can use the `repeat-last` parameter:
+```typ
+{{#include alternatives-repeat-last.typ:6:9}}
+```
+resulting in
+
+![alternatives-repeat-last](alternatives-repeat-last.png)
+
+### Positioning
By default, all elements that enter an `#alternatives` command are aligned at
the bottom left corner.
This might not always be the desired or the most pleasant way to position it, so
@@ -49,6 +61,62 @@ makes the mathematical terms look better positioned:
![alternatives-position](alternatives-position.png)
-Similar to `#one-by-one` and `#line-by-line`, `#alternatives` also has an optional
-`start` argument that works just the same as for the other two.
+All functions described on this page have such a `position` argument.
+
+Similar to `#one-by-one`, `#alternatives` also has an optional `start` argument
+that works just the same.
+
+## `#alternatives-match`
+`#alternatives` has a couple of "cousins" that might be more convenient in some
+situations.
+The first one is `#alternatives-match` that has a name inspired by match-statements
+in many functional programming languages.
+The idea is that you give it a dictionary mapping from subslides to content:
+```typ
+{{#include alternatives-match.typ:6:9}}
+```
+resulting in
+
+![alternatives-match](alternatives-match.png)
+
+**Note** that it is your responsibility to make sure that the subslide sets are
+mutually disjoint.
+
+## `#alternatives-cases`
+You can use this function if you want to have one piece of content that changes
+only slightly depending of what "case" of subslides you are in.
+So instead of
+```typ
+{{#include alternatives-cases.typ:6:14}}
+```
+you can avoid duplication and write
+```typ
+{{#include alternatives-cases.typ:18:21}}
+```
+using a function that maps the current "case" to content, resulting in
+
+![alternatives-cases](alternatives-cases.png)
+
+**Note** that the cases are 0-indexed (as are Typst arrays).
+
+
+## `#alternatives-fn`
+Finally, you can have very fine-grained control over the content depending on
+the current subslide by using `#alternatives-fn`.
+It accepts a function (hence the name) that maps the current subslide index to
+some content.
+
+Similar to `#alternatives`, it accepts an optional `start` parameter that has a
+default of `1`.
+`#alternatives-fn` only knows for how long to display something, though, if you
+provide either the number of subslides (`count` parameter) or the last subslide
+index (`end` parameter).
+So exactly one of them is necessary.
+
+For example:
+```typ
+{{#include alternatives-fn.typ:6:8}}
+```
+resulting in
+![alternatives-fn](alternatives-fn.png)
diff --git a/book/src/dynamic/alternatives.png b/book/src/dynamic/alternatives.png
index c93f837..34e0edb 100644
Binary files a/book/src/dynamic/alternatives.png and b/book/src/dynamic/alternatives.png differ
diff --git a/book/src/dynamic/cover.md b/book/src/dynamic/cover.md
index 18dbb10..cefd82a 100644
--- a/book/src/dynamic/cover.md
+++ b/book/src/dynamic/cover.md
@@ -1,6 +1,6 @@
# Cover mode
-Covered content (using `#uncover`, `#one-by-one`, `#line-by-line`, or `pause`)
-is completely invisible, by default.
+Covered content (using `#uncover`, `#one-by-one`, `#line-by-line`, or
+`#{list|enum|terms}-one-by-one`) is completely invisible, by default.
You can decide to make it visible but less prominent using the optional `mode`
argument to each of those functions.
The `mode` argument takes two different values: `"invisible"` (the default) and
@@ -10,7 +10,7 @@ With `mode: "transparent"`, text is printed in a light gray.
Use it as follows:
```typ
-{{#include cover.typ:6:16}}
+{{#include cover.typ:6:15}}
```
resulting in
diff --git a/book/src/dynamic/cover.png b/book/src/dynamic/cover.png
index 397bc8f..fe47e02 100644
Binary files a/book/src/dynamic/cover.png and b/book/src/dynamic/cover.png differ
diff --git a/book/src/dynamic/cover.typ b/book/src/dynamic/cover.typ
index 9f3bba1..85fbefc 100644
--- a/book/src/dynamic/cover.typ
+++ b/book/src/dynamic/cover.typ
@@ -3,7 +3,7 @@
#set text(size: 30pt)
#polylux-slide[
-#uncover("3-4", mode: "transparent")[abc]
+#uncover(3, mode: "transparent")[abc]
#one-by-one(start: 2, mode: "transparent")[def ][ghi]
@@ -12,6 +12,5 @@
- mno
]
-#show: pause(4, mode: "transparent")
-pqr
+#enum-one-by-one(mode: "transparent", tight: false)[pqr][stu][vwx]
]
diff --git a/book/src/dynamic/enum-one-by-one.png b/book/src/dynamic/enum-one-by-one.png
new file mode 100644
index 0000000..db08a22
Binary files /dev/null and b/book/src/dynamic/enum-one-by-one.png differ
diff --git a/book/src/dynamic/enum-one-by-one.typ b/book/src/dynamic/enum-one-by-one.typ
new file mode 100644
index 0000000..766ff3f
--- /dev/null
+++ b/book/src/dynamic/enum-one-by-one.typ
@@ -0,0 +1,7 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 50pt)
+
+#polylux-slide[
+#enum-one-by-one(numbering: "i)", number-align: start)[first][second][third]
+]
diff --git a/book/src/dynamic/handout.png b/book/src/dynamic/handout.png
index fb55995..6997906 100644
Binary files a/book/src/dynamic/handout.png and b/book/src/dynamic/handout.png differ
diff --git a/book/src/dynamic/helper.md b/book/src/dynamic/helper.md
index d15c0e1..2fe5c49 100644
--- a/book/src/dynamic/helper.md
+++ b/book/src/dynamic/helper.md
@@ -4,8 +4,8 @@ situations for which helper functions are provided.
We call them "higher level" because they use `#only` and `#uncover` under the
hood and operate on larger pieces of content.
-For the common case of succesively revealing content, there are `#one-by-one`,
-`#line-by-line`, and `pause`.
-For substituting content, we have `#alternatives`.
+For the common case of succesively revealing content, there are `#pause` and
+`#one-by-one` and its friends.
+For substituting content, we have `#alternatives` in different variants.
The following sections will describe these functions in detail.
diff --git a/book/src/dynamic/line-by-line.png b/book/src/dynamic/line-by-line.png
index 75b381f..4031e64 100644
Binary files a/book/src/dynamic/line-by-line.png and b/book/src/dynamic/line-by-line.png differ
diff --git a/book/src/dynamic/list-one-by-one.png b/book/src/dynamic/list-one-by-one.png
new file mode 100644
index 0000000..e1219ee
Binary files /dev/null and b/book/src/dynamic/list-one-by-one.png differ
diff --git a/book/src/dynamic/list-one-by-one.typ b/book/src/dynamic/list-one-by-one.typ
new file mode 100644
index 0000000..393bcb9
--- /dev/null
+++ b/book/src/dynamic/list-one-by-one.typ
@@ -0,0 +1,7 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 50pt)
+
+#polylux-slide[
+#list-one-by-one(marker: [--], tight: false)[first][second][third]
+]
diff --git a/book/src/dynamic/obo-lbl.md b/book/src/dynamic/obo-lbl.md
index 2a7ec75..b795314 100644
--- a/book/src/dynamic/obo-lbl.md
+++ b/book/src/dynamic/obo-lbl.md
@@ -1,13 +1,9 @@
-# `#one-by-one` and `#line-by-line`
-Consider some code like the following:
-```typ
-#uncover("1-")[first ]
-#uncover("2-")[second ]
-#uncover("3-")[third]
-```
-The goal here is to uncover parts of the slide one by one, so that an increasing
-amount of content is shown.
-A shorter but equivalent way would be to write
+# More sophisticated piecewise revealing
+## `#one-by-one`
+`#pause` may be considered syntactically a bit surprising by some although (or
+because) it is very convenient to use.
+If you prefer to signal the grouping of content appearing together syntactically
+by using scopes, you can use `#one-by-one`:
```typ
{{#include one-by-one.typ:6}}
```
@@ -15,15 +11,8 @@ resulting in
![one-by-one](one-by-one.png)
-And what about this?
-```typ
-#uncover("3-")[first ]
-#uncover("4-")[second ]
-#uncover("5-")[third]
-```
-Now, we still want to uncover certain elements one after the other but starting
-on subslide 3.
-We can use the optional `start` argument of `#one-by-one` for that:
+If we still want to uncover certain elements one after the other but starting
+on a later subslide, we can use the optional `start` argument of `#one-by-one`:
```typ
{{#include one-by-one-start.typ:6}}
```
@@ -31,10 +20,16 @@ resulting in
![one-by-one-start](one-by-one-start.png)
+This optional `start` argument exists for all functions displayed on this page.
+
+
+## `#line-by-line`
`#one-by-one` is especially useful for arbitrary contents that you want to display
in that manner.
-Often, you just want to do that with very simple elements, however.
-A very frequent use case are bullet lists.
+Sometimes, it produces a bit too much syntactical noise again with
+all the brackets between content, though.
+That is especially true if each piece fits into a single line, as for example
+for a simple bullet list.
Instead of
```typ
#one-by-one[
@@ -57,6 +52,50 @@ The content provided as an argument to `#line-by-line` is parsed as a `sequence`
by Typst with one element per line (hence the name of this function).
We then simply iterate over that `sequence` as if it were given to `#one-by-one`.
-Note that there also is an optional `start` argument for `#line-by-line`, which
-works just the same as for `#one-by-one`.
+## `#list-one-by-one`
+
+What if you want a more customized bullet list, though?
+The code above produces a tight list, for example, and maybe you do not want that.
+All your needs are covered by the `#list-one-by-one` function:
+```typ
+{{#include list-one-by-one.typ:6}}
+```
+resulting in
+
+![list-one-by-one](list-one-by-one.png)
+
+As you can see, you can provide any arguments that the
+[`list`](https://typst.app/docs/reference/layout/list/) function accepts.
+
+## `#enum-one-by-one`
+
+Analogously, there is the same thing for enums, accepting the same arguments as
+[`enum`](https://typst.app/docs/reference/layout/enum/):
+```typ
+{{#include enum-one-by-one.typ:6}}
+```
+resulting in
+
+![enum-one-by-one](enum-one-by-one.png)
+
+## `#terms-one-by-one`
+
+And finally we have a function to produce a
+[term list](https://typst.app/docs/reference/layout/terms/):
+
+```typ
+{{#include terms-one-by-one.typ:6}}
+```
+resulting in
+
+![terms-one-by-one](terms-one-by-one.png)
+
+**Note** that `#list-one-by-one` and `#enum-one-by-one` expect only the body of
+the individual items while you need to provide an actual term item (using the
+`/ term: description` syntax) to `#terms-one-by-one`.
+Also, you will realise that the bullet markers, the numbers, and the terms in
+the lists, enums, and term lists are not hidden for technical reasons, respectively.
+You can truly consider this either a bug or a feature...
+(This could be "fixed" for enums and term lists, so file an issue on GitHub if
+this bothers you a lot!)
diff --git a/book/src/dynamic/one-by-one-start.png b/book/src/dynamic/one-by-one-start.png
index d5a729b..9347f6e 100644
Binary files a/book/src/dynamic/one-by-one-start.png and b/book/src/dynamic/one-by-one-start.png differ
diff --git a/book/src/dynamic/one-by-one-start.typ b/book/src/dynamic/one-by-one-start.typ
index 371de49..5ea3ecf 100644
--- a/book/src/dynamic/one-by-one-start.typ
+++ b/book/src/dynamic/one-by-one-start.typ
@@ -3,5 +3,5 @@
#set text(size: 50pt)
#polylux-slide[
-#one-by-one(start: 3)[first ][second ][third]
+#one-by-one(start: 3)[This ][came ][pretty late.]
]
diff --git a/book/src/dynamic/one-by-one.png b/book/src/dynamic/one-by-one.png
index 39fdf0d..aa068b7 100644
Binary files a/book/src/dynamic/one-by-one.png and b/book/src/dynamic/one-by-one.png differ
diff --git a/book/src/dynamic/one-by-one.typ b/book/src/dynamic/one-by-one.typ
index 8d7b839..ac71dd5 100644
--- a/book/src/dynamic/one-by-one.typ
+++ b/book/src/dynamic/one-by-one.typ
@@ -3,5 +3,5 @@
#set text(size: 50pt)
#polylux-slide[
-#one-by-one[first ][second ][third]
+#one-by-one[Do you know ][$pi$ ][to a thousand decimal places?]
]
diff --git a/book/src/dynamic/only-uncover.png b/book/src/dynamic/only-uncover.png
index c736a52..389f167 100644
Binary files a/book/src/dynamic/only-uncover.png and b/book/src/dynamic/only-uncover.png differ
diff --git a/book/src/dynamic/pause.md b/book/src/dynamic/pause.md
index 4d6fe19..dedbf3a 100644
--- a/book/src/dynamic/pause.md
+++ b/book/src/dynamic/pause.md
@@ -1,49 +1,26 @@
-# `pause` as an alternative to `#one-by-one`
-There is yet another way to solve the same problem as `#one-by-one`.
+# `pause` to reveal content piece by piece
+Consider some code like the following:
+```typ
+#uncover("1-")[first ]
+#uncover("2-")[second ]
+#uncover("3-")[third]
+```
+The goal here is to uncover parts of the slide one by one, so that an increasing
+amount of content is shown, but we don't want to specify all subslide indices
+manually, ideally.
+
If you have used the LaTeX beamer package before, you might be familiar with the
`\pause` command.
It makes everything after it on that slide appear on the next subslide.
-
-Remember that the concept of "do something with everything after it" is covered
-by the `#show: ...` mechanism in Typst.
-We exploit that to use the `pause` function in the following way.
+In Polylux, this works very similar with `#pause`, so we can equivalently write
+the above code as:
```typ
-{{#include pause.typ:6:12}}
+{{#include pause.typ:6}}
```
-This would be equivalent to:
-```typ
-#one-by-one[
- Show this first.
-][
- Show this later.
-][
- Show this even later.
-][
- That took aaaages!
-]
-```
-and results in
+This results in
![pause](pause.png)
-It is obvious that `pause` only brings an advantage over `#one-by-one` when you
-want to distribute a lot of code onto different subslides.
-
-**Hint:**
-You might be annoyed by having to manually number the pauses as in the code
-above.
-You can diminish that issue a bit by using a counter variable:
-```typ
-Show this first.
-#let pc = 1 // `pc` for pause counter
-#{ pc += 1 } #show: pause(pc)
-Show this later.
-#{ pc += 1 } #show: pause(pc)
-Show this even later.
-#{ pc += 1 } #show: pause(pc)
-That took aaaages!
-```
-This has the advantage that every `pause` line looks identical and you can move
-them around arbitrarily.
-In later versions of this template, there could be a nicer solution to this
-issue, hopefully.
+`#pause` should mainly be used when you want to distribute a lot of code onto
+different subslides.
+For smaller pieces, consider one of the functions described next.
diff --git a/book/src/dynamic/pause.png b/book/src/dynamic/pause.png
index ce75d70..319f2e8 100644
Binary files a/book/src/dynamic/pause.png and b/book/src/dynamic/pause.png differ
diff --git a/book/src/dynamic/pause.typ b/book/src/dynamic/pause.typ
index a1dcec9..cc02b19 100644
--- a/book/src/dynamic/pause.typ
+++ b/book/src/dynamic/pause.typ
@@ -3,11 +3,5 @@
#set text(size: 50pt)
#polylux-slide[
-Show this first.
-#show: pause(2)
-Show this later.
-#show: pause(3)
-Show this even later.
-#show: pause(4)
-That took aaaages!
+first #pause second #pause third
]
diff --git a/book/src/dynamic/poor-alternatives.png b/book/src/dynamic/poor-alternatives.png
index f66a4fc..beb75c3 100644
Binary files a/book/src/dynamic/poor-alternatives.png and b/book/src/dynamic/poor-alternatives.png differ
diff --git a/book/src/dynamic/rule-array.png b/book/src/dynamic/rule-array.png
index 05494e5..283bcc9 100644
Binary files a/book/src/dynamic/rule-array.png and b/book/src/dynamic/rule-array.png differ
diff --git a/book/src/dynamic/rule-interval.png b/book/src/dynamic/rule-interval.png
index 5725310..84c2b31 100644
Binary files a/book/src/dynamic/rule-interval.png and b/book/src/dynamic/rule-interval.png differ
diff --git a/book/src/dynamic/rule-string.png b/book/src/dynamic/rule-string.png
index 0239a0e..0159f71 100644
Binary files a/book/src/dynamic/rule-string.png and b/book/src/dynamic/rule-string.png differ
diff --git a/book/src/dynamic/terms-one-by-one.png b/book/src/dynamic/terms-one-by-one.png
new file mode 100644
index 0000000..cfa6641
Binary files /dev/null and b/book/src/dynamic/terms-one-by-one.png differ
diff --git a/book/src/dynamic/terms-one-by-one.typ b/book/src/dynamic/terms-one-by-one.typ
new file mode 100644
index 0000000..35d13e6
--- /dev/null
+++ b/book/src/dynamic/terms-one-by-one.typ
@@ -0,0 +1,7 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 50pt)
+
+#polylux-slide[
+#terms-one-by-one(separator: [~---~])[/ first: 1st][/ second: 2nd][/ third: 3rd]
+]
diff --git a/book/src/external/external.md b/book/src/external/external.md
new file mode 100644
index 0000000..182dbf4
--- /dev/null
+++ b/book/src/external/external.md
@@ -0,0 +1,6 @@
+# External tools
+Most users will only come across Typst itself and some off-the-shelf PDF viewer.
+However, there are some additional tools that might come in handy and Polylux
+supports some of their special needs.
+
+So far, this support is limited to the pdfpc presentation tool.
diff --git a/book/src/external/pdfpc.md b/book/src/external/pdfpc.md
new file mode 100644
index 0000000..67dde35
--- /dev/null
+++ b/book/src/external/pdfpc.md
@@ -0,0 +1,140 @@
+# pdfpc
+
+[pdfpc](https://pdfpc.github.io/) is a "presenter console with multi-monitor
+support for PDF-files".
+That means, you can use it to display slides in the form of PDF-pages and also
+have some of the nice features known from, for example, PowerPoint.
+Check out their website to learn more.
+
+When pdfpc is provided a special `.pdfpc` file containing some JSON data, it can
+use that to enhance the user experience by correctly handling overlay slides,
+displaying speaker notes, setting up a specific timer, and more.
+While you can write this file by hand or use the pdfpc-internal features to edit
+it, some might find it more convenient to have all data about their presentation
+in one place, i.e. the Typst source file.
+Polylux allows you to do that.
+
+## Adding metadata to the Typst source
+
+Polylux exports the `pdfpc` module that comes with a bunch of useful functions
+that do not actually add any content to the produced PDF but instead insert
+metadata that can later be extracted from the document.
+
+### Speaker notes
+This is possibly the most useful feature of pdfpc.
+Using the function `#pdfpc.speaker-note` inside a slide, you can add a note to
+that slide that will only be visible to the speaker in pdfpc.
+It accepts either a string:
+```typ
+#pdfpc.speaker-note("This is a note that only the speaker will see.")
+```
+or a `raw` block:
+````typ
+#pdfpc.speaker-note(
+ ```md
+ # My notes
+ Did you know that pdfpc supports Markdown notes? _So cool!_
+ ```
+)
+````
+Note that you can only specify one note per slide (only the first one will
+survive if you use more than one.)
+
+### End slide
+Sometimes the last slide in your presentation is not really the one you want to
+end with.
+Say, you have some bibliography or appendix for the sake of completeness after
+your "I thank my mom and everyone who believed in me"-slide.
+
+With a simple `pdfpc.end-slide` inside any slide you can tell pdfpc that this is
+the last slide you usually want to show and hitting the `End` key will jump there.
+
+### Save a slide
+Similarly, there is a feature in pdfpc to bookmark a specific slide (and you can
+jump to it using `Shift + M`).
+In your Typst source, you can choose that slide by putting `pdfpc.save-slide`
+inside it.
+
+### Hide slides
+If you want to keep a certain slide in your presentation (just in case) but don't
+normally intend to show it, you can hide it inside pdfpc.
+It will be skipped during the presentation but it is still available in the
+overview.
+You can use `pdfpc.hidden-slide` in your Typst source to mark a slide as hidden.
+
+### Configure pdfpc
+The previous commands are all supposed to be used _inside_ a slide.
+To perform some additional global configuration, you can use `pdfpc.config()`
+_before_ any of the slides (it will not be recognised otherwise).
+
+It accepts the following optional keyword arguments:
+
+- `duration-minutes`: how many minutes (a number) the presentation is supposed
+ to take, affects the timer in pdfpc
+- `start-time`: wall-clock time when the presentation is supposed to start, either
+ as a `datetime(hour: ..., minute: ..., second: ...)` or as a string in the
+ `HH:MM` format
+- `end-time`: same as `start-time` but when the presentation is supposed to end
+- `last-minutes`: how many minutes (a number) before the time runs out the timer
+ is supposed to change its colour as a warning
+- `note-font-size`: the font size (a number) the speaker notes are displayed in
+- `disable-markdown`: whether or not to disable rendering the notes as markdown
+ (a bool), default `false`
+- `default-transition`: the transition to use between subsequent slides, must be
+ given as a dictionary with (potentially) the following keys:
+ - `type`: one of `"replace"` (default), `"push"`, `"blinds"`, `"box"`,
+ `"cover"`, `"dissolve"`, `"fade"`, `"glitter"`, `"split"`, `"uncover"`,
+ `"wipe"`
+ - `duration-seconds`: the duration of the transition in seconds (a number)
+ - `angle`: in which angle the transition moves, one of `ltr`, `rtl`, `ttb`,
+ and `btt` (see [the `#stack` function](https://typst.app/docs/reference/layout/stack/#parameters-dir))
+ - `alignment`: whether the transition is performed horizontally or vertically,
+ one of `"horizontal"` and `"vertical"`
+ - `direction`: whether the transition is performed inward or outward, one of
+ `"inward"` and `"outward"`
+
+ Not all combinations of values are necessary or make sense for all transitions,
+ of course.
+
+## Extracting the data: `polylux2pdfpc`
+As mentioned above, the functions from the `pdfpc` module don't alter the produced
+PDF itself.
+Instead, we need some other way to extract their data.
+You could, in principle, do that by hand using the `typst query` CLI and then
+assemble the correct `.pdfpc` file yourself.
+However, this tedious task is better solved by the `polylux2pdfpc` tool.
+
+### Installation
+If you have [Rust](https://www.rust-lang.org/tools/install) installed, you can
+simply run
+```sh
+cargo install --git https://github.com/andreasKroepelin/polylux/ --branch release
+```
+If you use Arch Linux btw, you can also install `polylux2pdfpc` from the AUR
+package [polylux2pdfpc-git](https://aur.archlinux.org/packages/polylux2pdfpc-git)
+(thank you to Julius Freudenberger!)
+
+### Usage
+You invoke `polylux2pdfpc` with the same arguments you would also give to `typst
+compile` when you wanted to build your slides.
+For example, say you have a file called `talk.typ` in the folder `thesis` that
+has some global utility files or so, you
+would compile it using
+```sh
+typst compile --root .. thesis/talk.typ
+```
+and extract the pdfpc data using
+```sh
+polylux2pdfpc --root .. thesis/talk.typ
+```
+
+Internally, `polylux2pdfpc` runs `typst query`, collects all the pdfpc-related
+metadata and then writes a `.pdfpc` file that equals the input file up to the
+suffix.
+In our example with `thesis/talk.typ`, we obtain `thesis/talk.pdfpc`.
+Since `typst compile` produced `thesis/talk.pdf`, you can now simply open the PDF
+in pdpfc:
+```sh
+pdfpc thesis/talk.pdf
+```
+and it will automatically recognise the `.pdfpc` file.
diff --git a/book/src/getting-started.md b/book/src/getting-started.md
index 58c0dc5..53905e0 100644
--- a/book/src/getting-started.md
+++ b/book/src/getting-started.md
@@ -4,7 +4,7 @@ You can find this package in the
[official Typst package repository](https://github.com/typst/packages).
To use it, start your document with
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include IMPORT.typ}}
```
You now have two options:
1. use the features of polylux but define every visual aspect yourself,
diff --git a/book/src/minimal.png b/book/src/minimal.png
index 9929192..215426e 100644
Binary files a/book/src/minimal.png and b/book/src/minimal.png differ
diff --git a/book/src/polylux.md b/book/src/polylux.md
index e0172af..0331428 100644
--- a/book/src/polylux.md
+++ b/book/src/polylux.md
@@ -29,6 +29,13 @@ The German term for projector is *beamer*, and now you might understand how it
all comes together.
(The original author of the aforementioned LaTeX package is German as well.)
+## About this book
+This book documents all features currently implemented in Polylux.
+Specifically, it describes the state of the package as it is pulished to the
+Typst package registry.
+The `main` branch of the Polylux repository may contain features not documented
+here.
+
## Contributing
This package is free and open source.
You can find the code on [GitHub](https://github.com/andreasKroepelin/polylux)
diff --git a/book/src/themes/gallery/bipartite.md b/book/src/themes/gallery/bipartite.md
index 9bada32..2382b6f 100644
--- a/book/src/themes/gallery/bipartite.md
+++ b/book/src/themes/gallery/bipartite.md
@@ -10,7 +10,7 @@ rather on the "artsy" than functional side.
Use it via
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
#import themes.bipartite: *
#show: bipartite-theme.with(...)
@@ -84,6 +84,6 @@ Does not display a slide title.
## Example code
The image at the top is created by the following code:
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
{{#include bipartite.typ:3:}}
```
diff --git a/book/src/themes/gallery/bipartite.png b/book/src/themes/gallery/bipartite.png
index 4199421..5cc92cf 100644
Binary files a/book/src/themes/gallery/bipartite.png and b/book/src/themes/gallery/bipartite.png differ
diff --git a/book/src/themes/gallery/clean.md b/book/src/themes/gallery/clean.md
index c1f1253..bf6d852 100644
--- a/book/src/themes/gallery/clean.md
+++ b/book/src/themes/gallery/clean.md
@@ -7,7 +7,7 @@ to be an off-the-shelf solution that fits many use cases.
Use it via
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
#import themes.clean: *
#show: clean-theme.with(...)
@@ -56,25 +56,16 @@ Does not accept additional content.
```typ
#slide(...)[
...
-][
- ...
]
```
Decorates the provided content with a header containing the current section (if
any), the short title of the presentation, and the logo; and a footer containing
some custom text and the slide number.
-Accepts an arbitrary amount of content blocks, they are placed next to each other
-as columns.
-Configure using the `columns` and `gutter` keyword arguments.
-
Pass the slide title as a keyword argument `title`.
Accepts the following keyword arguments:
- `title`: title of the slide, default: `none`,
-- `columns`: propagated to `grid` for placing the body columns, default: array
- filled with as many `1fr` as there are content blocks
-- `gutter`: propagated to `grid` for placing the body columns, default: `1em`
---
@@ -104,6 +95,6 @@ Use `#polylux-outline()` to display all sections, similarly to how you would use
## Example code
The image at the top is created by the following code:
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
{{#include clean.typ:3:}}
```
diff --git a/book/src/themes/gallery/clean.png b/book/src/themes/gallery/clean.png
index ef425af..950a463 100644
Binary files a/book/src/themes/gallery/clean.png and b/book/src/themes/gallery/clean.png differ
diff --git a/book/src/themes/gallery/clean.typ b/book/src/themes/gallery/clean.typ
index a748c2d..bba4b9f 100644
--- a/book/src/themes/gallery/clean.typ
+++ b/book/src/themes/gallery/clean.typ
@@ -24,12 +24,12 @@
#new-section-slide("The new section")
-#slide(title: [Slide with multiple columns])[
- #lorem(20)
-][
- #lorem(10)
-][
- #lorem(30)
+#slide(title: "Another slide")[
+ Note that you can see the section title at the top.
+
+ The rest of this slide will fill more than one page!
+
+ #lorem(100)
]
#focus-slide[
diff --git a/book/src/themes/gallery/metropolis.md b/book/src/themes/gallery/metropolis.md
index 3761725..9eb11c4 100644
--- a/book/src/themes/gallery/metropolis.md
+++ b/book/src/themes/gallery/metropolis.md
@@ -8,7 +8,7 @@ created by Matthias Vogelgesang.
Use it via
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
#import themes.metropolis: *
#show: metropolis-theme.with(...)
@@ -93,6 +93,6 @@ displays a table of contents with all sections.
## Example code
The image at the top is created by the following code:
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
{{#include metropolis.typ:3:}}
```
diff --git a/book/src/themes/gallery/metropolis.png b/book/src/themes/gallery/metropolis.png
index d81e3a9..3932cf0 100644
Binary files a/book/src/themes/gallery/metropolis.png and b/book/src/themes/gallery/metropolis.png differ
diff --git a/book/src/themes/gallery/simple.md b/book/src/themes/gallery/simple.md
index f73019c..4f9f077 100644
--- a/book/src/themes/gallery/simple.md
+++ b/book/src/themes/gallery/simple.md
@@ -8,7 +8,7 @@ freely.
Use it via
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
#import themes.simple: *
#show: simple-theme.with(...)
@@ -81,6 +81,6 @@ Not suitable for content that exceeds one page.
## Example code
The image at the top is created by the following code:
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
{{#include simple.typ:3:}}
```
diff --git a/book/src/themes/gallery/simple.png b/book/src/themes/gallery/simple.png
index b8a5968..5790a29 100644
Binary files a/book/src/themes/gallery/simple.png and b/book/src/themes/gallery/simple.png differ
diff --git a/book/src/themes/gallery/simple.typ b/book/src/themes/gallery/simple.typ
index b916d55..ecd5b6c 100644
--- a/book/src/themes/gallery/simple.typ
+++ b/book/src/themes/gallery/simple.typ
@@ -39,7 +39,6 @@
== Dynamic slide
Did you know that...
- #uncover(2)[
- ...you can see the current section at the top of the slide?
- ]
+ #pause
+ ...you can see the current section at the top of the slide?
]
diff --git a/book/src/themes/gallery/university.md b/book/src/themes/gallery/university.md
index a1c1b6c..ec2a1d4 100644
--- a/book/src/themes/gallery/university.md
+++ b/book/src/themes/gallery/university.md
@@ -5,11 +5,10 @@
This theme offers a simple yet versatile design, allowing for easy customization
and flexibility. Additionally, it incorporates a progress bar at the top, which
displays the current status of the presentation.
-`university` also makes working with mulit-column content very easy.
Use it via
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
#import themes.university: *
#show: university-theme.with(...)
@@ -39,7 +38,7 @@ Text is configured to have a base font size of 25 pt.
regular sides, default: `true`
## Slide functions
-`metropolis` provides the following custom slide functions:
+`university` provides the following custom slide functions:
```typ
#title-slide(...)
@@ -63,8 +62,6 @@ Does not accept additional content.
```typ
#slide(...)[
...
-][
- ...
]
```
Decorates the provided content with a header containing a progress bar (optionally),
@@ -72,17 +69,10 @@ the slide title, and the current section (if any); and a footer containing short
forms of authors, title, and date, and the slide number.
Header and footer can also be overwritten by respective keyword arguments.
-Accepts an arbitrary amount of content blocks, they are placed next to each other
-as columns.
-Configure using the `columns` and `gutter` keyword arguments.
-
Pass the slide title as a keyword argument `title`.
Accepts the following keyword arguments:
- `title`: title of the slide, default: `none`,
-- `columns`: propagated to `grid` for placing the body columns, default: array
- filled with as many `1fr` as there are content blocks
-- `gutter`: propagated to `grid` for placing the body columns, default: `1em`
- `header`: custom content to overwrite default header
- `footer`: custom content to overwrite default footer
- `new-section`: name of the new section that starts here if not `none`, default:
@@ -137,6 +127,6 @@ Not suitable for content that exceeds one page.
## Example code
The image at the top is created by the following code:
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../../IMPORT.typ}}
{{#include university.typ:3:}}
```
diff --git a/book/src/themes/gallery/university.png b/book/src/themes/gallery/university.png
index ad41df1..253704f 100644
Binary files a/book/src/themes/gallery/university.png and b/book/src/themes/gallery/university.png differ
diff --git a/book/src/themes/gallery/university.typ b/book/src/themes/gallery/university.typ
index 5f6b71e..35d0472 100644
--- a/book/src/themes/gallery/university.typ
+++ b/book/src/themes/gallery/university.typ
@@ -21,13 +21,6 @@
#lorem(40)
]
-
-#slide(title: "A longer slide title with 2 columns")[
- #lorem(30)
-][
- #lorem(30)
-]
-
#focus-slide(background-img: image("background.svg"))[
*Another variant with an image in background...*
]
diff --git a/book/src/themes/helpers.md b/book/src/themes/helpers.md
index aa4fe6f..188301d 100644
--- a/book/src/themes/helpers.md
+++ b/book/src/themes/helpers.md
@@ -1,84 +1 @@
# Helpers for theme authors
-
-Let us have a look at some common use cases you run into as a theme author and
-what solutions are provided by the `helpers` and `logic` modules inside polylux.
-As a theme author, you have access to them due to the imports
-```typ
-#import "../logic.typ"
-#import "../helpers.typ"
-```
-(see previous page).
-
-## How much longer? 🥱
-
-There are a handfull of features that let you display the progress of the
-presentation.
-
-The most simple one is directly displaying the current slide number.
-Remember that each slide might produce an arbitrary amount of subslides, i.e.
-PDF pages, so we cannot rely on the builtin page counter.
-Instead, there is the `logical-slide` counter in the `logic` module.
-Therefore, you can use
-```typ
-#logic.logical-slide.display()
-```
-to see what the current slide number is.
-
-If you want to put that into relation to how many slides there are in total,
-you can also display
-```typ
-#helpers.last-slide-number
-```
-which is a short-hand way of getting the final value of `logic.logical-slide`.
-
-Note that both these things are content, though, so you can only display them
-and not calculate with the numbers.
-A common calculation you might want do to is finding their ratio, i.e. current
-slide number divided by total number of slides.
-To that end, you can use the function `helpers.polylux-progress`.
-You can pass a function to it that turns the current ratio into some content.
-For example:
-```typ
-#helpers.polylux-progress( ratio => [
- You already made it through #calc.round(ratio * 100) #sym.percent of the presentation!
-])
-```
-Some themes utilise this to display a little progress bar, for example.
-
-## Sections
-Another way of expressing where we are in a presentation is working with sections.
-In your theme, you can incorporate the following features from the `helpers`
-module:
-
-First, whenever a user wants to start a new section, you can call
-```typ
-#helpers.register-section(the-section-name)
-```
-with whatever name they specify.
-It is up to you to decide what kind of interface you provide for the user and
-how/if you visualise a new section, of course.
-
-Based on that, you can then display what section the presenter is currently in
-by using:
-```typ
-#helpers.current-section
-```
-If no section has been registered so far, this is empty content (`[]`).
-
-And finally, you might want to display some kind of overview over all the sections.
-This is achieved by:
-```typ
-#helpers.polylux-outline()
-```
-Unfortunately, it is hard to get the Typst-builtin `#outline` to work properly
-with slides, partly again due to how page numbers are kind of meaningless.
-`polylux-outline` is a good alternative to that and will return an `enum` with
-all the registered sections (ever, not only so far, so you can safely use it
-at the beginning of a presentation).
-
-`polylux-outline` has two optional keyword arguments:
-- `enum-args`: pass a dictionary that is propagated to
- [`enum` as keyword arguments](https://typst.app/docs/reference/layout/enum#parameters),
- for example `enum-args: (tight: false)`, default: `(:)`
-- `padding`: pass [something that `pad` accepts](https://typst.app/docs/reference/layout/pad#parameters),
- will be used to pad the `enum`, default: `0pt`
diff --git a/book/src/themes/science-slam.png b/book/src/themes/science-slam.png
index 149b87f..59c3003 100644
Binary files a/book/src/themes/science-slam.png and b/book/src/themes/science-slam.png differ
diff --git a/book/src/themes/themes.md b/book/src/themes/themes.md
index 8871863..2c8f9a6 100644
--- a/book/src/themes/themes.md
+++ b/book/src/themes/themes.md
@@ -20,7 +20,7 @@ First of all, all themes reside in the `themes` module inside polylux.
That means, if you want to employ, say, the `simple` theme, you add the following
to your regular `#import` line at the top:
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../IMPORT.typ}}
#import themes.simple: *
```
diff --git a/book/src/themes/your-own.md b/book/src/themes/your-own.md
index 31e42b5..efcc975 100644
--- a/book/src/themes/your-own.md
+++ b/book/src/themes/your-own.md
@@ -1,6 +1,6 @@
# Build your own theme
-Again, there is no right or wrong when it comes to how a polylux theme works.
+Again, there is no right or wrong when it comes to how a Polylux theme works.
If you consider building a theme that you would like to contribute to the
package ([which you are cordially invited to do!](https://github.com/andreasKroepelin/polylux/pulls)),
we kindly ask you to follow the convention presented before.
@@ -17,17 +17,17 @@ Depending on whether this is a theme for yourself or supposed to be part of
polylux, you do one of two things:
For yourself, you simply import polylux as always:
```typ
-#import "@preview/polylux:0.2.0": *
+{{#include ../IMPORT.typ}}
```
A theme that is shipped with polylux doesn't have to do that, and it shouldn't!
Otherwise circular imports can occur.
-Instead, you depend on the two files `logic.typ` and `helpers.typ`.
+Instead, you depend on the two files `logic.typ` and `utils/utils.typ`.
As your theme file `science-slam.typ` will be inside the `themes` directory, the
imports will be:
```typ
#import "../logic.typ"
-#import "../helpers.typ"
+#import "../utils/utils.typ"
```
Additionally, you have to make polylux know about your theme which you do by
adding
diff --git a/book/src/utils/fill-remaining.png b/book/src/utils/fill-remaining.png
new file mode 100644
index 0000000..706f891
Binary files /dev/null and b/book/src/utils/fill-remaining.png differ
diff --git a/book/src/utils/fill-remaining.typ b/book/src/utils/fill-remaining.typ
new file mode 100644
index 0000000..1d1d68a
--- /dev/null
+++ b/book/src/utils/fill-remaining.typ
@@ -0,0 +1,7 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 40pt)
+
+#polylux-slide[
+#fit-to-height(1fr)[BIG]
+]
diff --git a/book/src/utils/fit-to-height-width.png b/book/src/utils/fit-to-height-width.png
new file mode 100644
index 0000000..86f7316
Binary files /dev/null and b/book/src/utils/fit-to-height-width.png differ
diff --git a/book/src/utils/fit-to-height-width.typ b/book/src/utils/fit-to-height-width.typ
new file mode 100644
index 0000000..57ba195
--- /dev/null
+++ b/book/src/utils/fit-to-height-width.typ
@@ -0,0 +1,10 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 40pt)
+
+#polylux-slide[
+#fit-to-height(5cm, prescale-width: 300%, width: 50%)[
+ #set par(justify: true)
+ #lorem(200)
+]
+]
diff --git a/book/src/utils/fit-to-height.md b/book/src/utils/fit-to-height.md
new file mode 100644
index 0000000..74d4f9a
--- /dev/null
+++ b/book/src/utils/fit-to-height.md
@@ -0,0 +1,43 @@
+# Fit to height
+
+Suppose you have some content and some size constraints for it but the two don't
+match, i.e. the content does not have the size that you want.
+The function `#fit-to-height` can help you with that.
+
+It expects a height and some content and will try to scale the content such that
+it takes on the given height:
+
+```typ
+{{#include fill-remaining.typ:6}}
+```
+
+resulting in
+
+![fill-remaining](fill-remaining.png)
+
+Using `1fr` as the height is probably also the prime use case for this function,
+as it fills the remaining space of the slide with the given content.
+Anything else (like `5pt`, `3cm`, `4em` etc.) is possible as well, of course.
+
+## Adjusting the width
+To finetune the result of `#fit-to-height`, you have two optional parameters:
+- `width`: If specified, this determines the width of the content _after_ scaling.
+ So, if you want the scaled content to fill half the slide's width for example,
+ you can use `width: 50%`.
+ By default, the scaled content will be constrained by the slide's width and
+ will have less than the requested height if necessary.
+- `prescale-width`: This parameter allows you to make Typst's layouting assume
+ the given content is to be layouted in a container of a certain width _before_
+ scaling.
+ You can pretend the slide is twice as wide using `prescale-width: 200%`, for
+ example.
+
+We can illustrate that using the following example:
+
+```typ
+{{#include fit-to-height-width.typ:6:9}}
+```
+
+resulting in
+
+![fit-to-height-width](fit-to-height-width.png)
diff --git a/book/src/utils/progress.md b/book/src/utils/progress.md
new file mode 100644
index 0000000..f138580
--- /dev/null
+++ b/book/src/utils/progress.md
@@ -0,0 +1,35 @@
+# How much longer? 🥱
+
+There are a handfull of features that let you display the progress of the
+presentation.
+
+The most simple one is directly displaying the current slide number.
+Remember that each slide might produce an arbitrary amount of subslides, i.e.
+PDF pages, so we cannot rely on the builtin page counter.
+Instead, there is the `logical-slide` counter in the `logic` module.
+Therefore, you can use
+```typ
+#logic.logical-slide.display()
+```
+to see what the current slide number is.
+
+If you want to put that into relation to how many slides there are in total,
+you can also display
+```typ
+#utils.last-slide-number
+```
+which is a short-hand way of getting the final value of `logic.logical-slide`.
+
+Note that both these things are content, though, so you can only display them
+and not calculate with the numbers.
+A common calculation you might want do to is finding their ratio, i.e. current
+slide number divided by total number of slides.
+To that end, you can use the function `utils.polylux-progress`.
+You can pass a function to it that turns the current ratio into some content.
+For example:
+```typ
+#utils.polylux-progress( ratio => [
+ You already made it through #calc.round(ratio * 100) #sym.percent of the presentation!
+])
+```
+Some themes utilise this to display a little progress bar, for example.
diff --git a/book/src/utils/sections.md b/book/src/utils/sections.md
new file mode 100644
index 0000000..165e28a
--- /dev/null
+++ b/book/src/utils/sections.md
@@ -0,0 +1,40 @@
+# Sections
+Another way of expressing where we are in a presentation is working with sections.
+Usually, this is a topic that a theme will/should handle so **this page is
+addressed more towards theme authors**.
+
+In your theme, you can incorporate the following features from the `utils`
+module:
+
+First, whenever a user wants to start a new section, you can call
+```typ
+#utils.register-section(the-section-name)
+```
+with whatever name they specify.
+It is up to you to decide what kind of interface you provide for the user and
+how/if you visualise a new section, of course.
+
+Based on that, you can then display what section the presenter is currently in
+by using:
+```typ
+#utils.current-section
+```
+If no section has been registered so far, this is empty content (`[]`).
+
+And finally, you might want to display some kind of overview over all the sections.
+This is achieved by:
+```typ
+#utils.polylux-outline()
+```
+Unfortunately, it is hard to get the Typst-builtin `#outline` to work properly
+with slides, partly again due to how page numbers are kind of meaningless.
+`polylux-outline` is a good alternative to that and will return an `enum` with
+all the registered sections (ever, not only so far, so you can safely use it
+at the beginning of a presentation).
+
+`polylux-outline` has two optional keyword arguments:
+- `enum-args`: pass a dictionary that is propagated to
+ [`enum` as keyword arguments](https://typst.app/docs/reference/layout/enum#parameters),
+ for example `enum-args: (tight: false)`, default: `(:)`
+- `padding`: pass [something that `pad` accepts](https://typst.app/docs/reference/layout/pad#parameters),
+ will be used to pad the `enum`, default: `0pt`
diff --git a/book/src/utils/side-by-side-kwargs.png b/book/src/utils/side-by-side-kwargs.png
new file mode 100644
index 0000000..26c84e5
Binary files /dev/null and b/book/src/utils/side-by-side-kwargs.png differ
diff --git a/book/src/utils/side-by-side-kwargs.typ b/book/src/utils/side-by-side-kwargs.typ
new file mode 100644
index 0000000..9ac6a3e
--- /dev/null
+++ b/book/src/utils/side-by-side-kwargs.typ
@@ -0,0 +1,13 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 40pt)
+
+#polylux-slide[
+#side-by-side(gutter: 3mm, columns: (1fr, 2fr, 1fr))[
+ #rect(width: 100%, stroke: none, fill: aqua)
+][
+ #rect(width: 100%, stroke: none, fill: teal)
+][
+ #rect(width: 100%, stroke: none, fill: eastern)
+]
+]
diff --git a/book/src/utils/side-by-side.md b/book/src/utils/side-by-side.md
new file mode 100644
index 0000000..a78ea2d
--- /dev/null
+++ b/book/src/utils/side-by-side.md
@@ -0,0 +1,47 @@
+# Side by side
+To make good use of the space on a slide, you will often want to place content
+next to each other.
+For convenience, Polylux provides the function `#side-by-side` for this purpose.
+If you used
+```typ
+{{#include ../IMPORT.typ}}
+```
+you have it directly available.
+Otherwise you can get if from the `utils` module.
+
+It is basically a thin wrapper around the Typst function
+[`#grid`](https://typst.app/docs/reference/layout/grid/) but tailored
+towards this specific usecase.
+In its simplest form, you can use it as
+```typ
+{{#include side-by-side.typ:6:12}}
+```
+
+resulting in
+
+![side-by-side](side-by-side.png)
+
+As you can see, the content arguments you provide will be placed next to each
+other with equal proportions of width.
+A spacing (gutter) of `1em` will also be put between them.
+
+The widths and gutter can be configured using the `columns` and `gutter` optional
+arguments, respectively.
+They are propagated to `#grid` directly so you can look up possible values in
+its documentation
+([`gutter`](https://typst.app/docs/reference/layout/grid/#parameters-gutter)
+and [`columns`](https://typst.app/docs/reference/layout/grid/#parameters-columns)
+arguments).
+If not specified, they fall back to these defaults:
+- `gutter`: `1em`
+- `columns`: `(1fr,) * n` if you provided `n` content arguments, that means an
+ array with the value `1fr` repeated `n` times.
+
+A more complex example would therefore be:
+```typ
+{{#include side-by-side-kwargs.typ:6:12}}
+```
+
+resulting in
+
+![side-by-side-kwargs](side-by-side-kwargs.png)
diff --git a/book/src/utils/side-by-side.png b/book/src/utils/side-by-side.png
new file mode 100644
index 0000000..8bc67f9
Binary files /dev/null and b/book/src/utils/side-by-side.png differ
diff --git a/book/src/utils/side-by-side.typ b/book/src/utils/side-by-side.typ
new file mode 100644
index 0000000..57a4e6d
--- /dev/null
+++ b/book/src/utils/side-by-side.typ
@@ -0,0 +1,13 @@
+#import "../../../polylux.typ": *
+#set page(paper: "presentation-16-9")
+#set text(size: 40pt)
+
+#polylux-slide[
+#side-by-side[
+ #lorem(7)
+][
+ #lorem(10)
+][
+ #lorem(5)
+]
+]
diff --git a/book/src/utils/utils.md b/book/src/utils/utils.md
new file mode 100644
index 0000000..9d896a6
--- /dev/null
+++ b/book/src/utils/utils.md
@@ -0,0 +1,21 @@
+# Utility features
+
+Let us have a look at some common use cases you run into as either a theme
+author or as an end user producing slides and what solutions are provided by
+Polylux.
+
+Specifically, the functions discussed here reside in the `utils` and `logic`
+modules.
+They are exported by Polylux so if you (as someone making slides) imported it
+using
+```typ
+{{#include ../IMPORT.typ}}
+```
+you can direcly invoke them using `utils` and `logic`.
+As a theme author, you have access to them due to the imports
+```typ
+#import "../logic.typ"
+#import "../utils/utils.typ"
+```
+(see previous page).
+
diff --git a/examples/demo.pdfpc b/examples/demo.pdfpc
new file mode 100644
index 0000000..f1c82c8
--- /dev/null
+++ b/examples/demo.pdfpc
@@ -0,0 +1,339 @@
+{
+ "pages": [
+ {
+ "label": "1",
+ "idx": 0,
+ "overlay": 0
+ },
+ {
+ "label": "2",
+ "idx": 1,
+ "overlay": 0
+ },
+ {
+ "label": "3",
+ "overlay": 0,
+ "idx": 2
+ },
+ {
+ "idx": 3,
+ "label": "4",
+ "overlay": 0
+ },
+ {
+ "idx": 4,
+ "overlay": 0,
+ "label": "5"
+ },
+ {
+ "idx": 5,
+ "overlay": 0,
+ "label": "6"
+ },
+ {
+ "idx": 6,
+ "label": "7",
+ "overlay": 0
+ },
+ {
+ "idx": 7,
+ "label": "8",
+ "overlay": 0
+ },
+ {
+ "overlay": 1,
+ "label": "8",
+ "idx": 8,
+ "forcedOverlay": true
+ },
+ {
+ "label": "8",
+ "idx": 9,
+ "forcedOverlay": true,
+ "overlay": 2
+ },
+ {
+ "idx": 10,
+ "overlay": 0,
+ "label": "9"
+ },
+ {
+ "idx": 11,
+ "overlay": 0,
+ "label": "10"
+ },
+ {
+ "idx": 12,
+ "overlay": 1,
+ "forcedOverlay": true,
+ "label": "10"
+ },
+ {
+ "forcedOverlay": true,
+ "overlay": 2,
+ "idx": 13,
+ "label": "10"
+ },
+ {
+ "overlay": 0,
+ "idx": 14,
+ "label": "11"
+ },
+ {
+ "idx": 15,
+ "overlay": 1,
+ "label": "11",
+ "forcedOverlay": true
+ },
+ {
+ "overlay": 2,
+ "label": "11",
+ "forcedOverlay": true,
+ "idx": 16
+ },
+ {
+ "forcedOverlay": true,
+ "label": "11",
+ "overlay": 3,
+ "idx": 17
+ },
+ {
+ "overlay": 0,
+ "idx": 18,
+ "label": "12"
+ },
+ {
+ "forcedOverlay": true,
+ "overlay": 1,
+ "label": "12",
+ "idx": 19
+ },
+ {
+ "idx": 20,
+ "label": "12",
+ "forcedOverlay": true,
+ "overlay": 2
+ },
+ {
+ "forcedOverlay": true,
+ "overlay": 3,
+ "idx": 21,
+ "label": "12"
+ },
+ {
+ "overlay": 4,
+ "label": "12",
+ "forcedOverlay": true,
+ "idx": 22
+ },
+ {
+ "idx": 23,
+ "label": "12",
+ "overlay": 5,
+ "forcedOverlay": true
+ },
+ {
+ "forcedOverlay": true,
+ "idx": 24,
+ "overlay": 6,
+ "label": "12"
+ },
+ {
+ "label": "12",
+ "overlay": 7,
+ "idx": 25,
+ "forcedOverlay": true
+ },
+ {
+ "overlay": 0,
+ "idx": 26,
+ "label": "13"
+ },
+ {
+ "idx": 27,
+ "overlay": 1,
+ "forcedOverlay": true,
+ "label": "13"
+ },
+ {
+ "idx": 28,
+ "forcedOverlay": true,
+ "label": "13",
+ "overlay": 2
+ },
+ {
+ "forcedOverlay": true,
+ "idx": 29,
+ "overlay": 3,
+ "label": "13"
+ },
+ {
+ "label": "13",
+ "overlay": 4,
+ "forcedOverlay": true,
+ "idx": 30
+ },
+ {
+ "idx": 31,
+ "forcedOverlay": true,
+ "label": "13",
+ "overlay": 5
+ },
+ {
+ "idx": 32,
+ "overlay": 0,
+ "label": "14"
+ },
+ {
+ "overlay": 1,
+ "label": "14",
+ "forcedOverlay": true,
+ "idx": 33
+ },
+ {
+ "forcedOverlay": true,
+ "label": "14",
+ "overlay": 2,
+ "idx": 34
+ },
+ {
+ "label": "15",
+ "idx": 35,
+ "overlay": 0
+ },
+ {
+ "overlay": 1,
+ "forcedOverlay": true,
+ "label": "15",
+ "idx": 36
+ },
+ {
+ "forcedOverlay": true,
+ "overlay": 2,
+ "idx": 37,
+ "label": "15"
+ },
+ {
+ "idx": 38,
+ "forcedOverlay": true,
+ "overlay": 3,
+ "label": "15"
+ },
+ {
+ "label": "16",
+ "idx": 39,
+ "overlay": 0
+ },
+ {
+ "idx": 40,
+ "forcedOverlay": true,
+ "label": "16",
+ "overlay": 1
+ },
+ {
+ "idx": 41,
+ "label": "16",
+ "forcedOverlay": true,
+ "overlay": 2
+ },
+ {
+ "label": "16",
+ "forcedOverlay": true,
+ "overlay": 3,
+ "idx": 42
+ },
+ {
+ "label": "17",
+ "overlay": 0,
+ "idx": 43
+ },
+ {
+ "forcedOverlay": true,
+ "idx": 44,
+ "overlay": 1,
+ "label": "17"
+ },
+ {
+ "idx": 45,
+ "overlay": 2,
+ "forcedOverlay": true,
+ "label": "17"
+ },
+ {
+ "overlay": 0,
+ "label": "18",
+ "idx": 46
+ },
+ {
+ "overlay": 0,
+ "label": "19",
+ "idx": 47
+ },
+ {
+ "label": "20",
+ "overlay": 0,
+ "idx": 48
+ },
+ {
+ "idx": 49,
+ "overlay": 0,
+ "label": "21"
+ },
+ {
+ "overlay": 0,
+ "idx": 50,
+ "label": "22"
+ },
+ {
+ "overlay": 0,
+ "label": "23",
+ "idx": 51
+ },
+ {
+ "label": "24",
+ "idx": 52,
+ "overlay": 0
+ },
+ {
+ "overlay": 0,
+ "idx": 53,
+ "label": "25"
+ },
+ {
+ "overlay": 0,
+ "label": "26",
+ "idx": 54
+ },
+ {
+ "overlay": 0,
+ "idx": 55,
+ "label": "27"
+ },
+ {
+ "idx": 56,
+ "label": "28",
+ "overlay": 0
+ },
+ {
+ "idx": 57,
+ "overlay": 0,
+ "label": "29"
+ },
+ {
+ "idx": 58,
+ "label": "30",
+ "overlay": 0
+ },
+ {
+ "label": "31",
+ "idx": 59,
+ "overlay": 0
+ },
+ {
+ "idx": 60,
+ "label": "32",
+ "overlay": 0
+ }
+ ],
+ "pdfpcFormat": 2
+}
\ No newline at end of file
diff --git a/examples/demo.typ b/examples/demo.typ
index b0d4ef8..96af3c9 100644
--- a/examples/demo.typ
+++ b/examples/demo.typ
@@ -68,22 +68,18 @@
You can also see the slide number there.
]
+
#new-section-slide("Dynamic content")
#slide(title: [A dynamic slide with `pause`s])[
Sometimes we don't want to display everything at once.
- #let pc = 1
- #{ pc += 1 } #show: pause(pc)
+ #pause
- That's what the `pause` function is there for!
- Use it as
- ```typ
- #show: pause(n)
- ```
- #{ pc += 1 } #show: pause(pc)
+ That's what the `#pause` function is there for!
+ #pause
- It makes everything after it appear at the `n`-th subslide.
+ It makes everything after it appear at the next subslide.
#text(.6em)[(Also note that the slide number does not change while we are here.)]
]
@@ -92,7 +88,7 @@
When `#pause` does not suffice, you can use more advanced commands to show
or hide content.
- These are your options:
+ These are some of your options:
- `#uncover`
- `#only`
- `#alternatives`
@@ -255,6 +251,32 @@
`start` is again optional and defaults to `1`.
]
+#slide(title: [`#list-one-by-one` and Co: when `#line-by-line` doesn't suffice])[
+ While `#line-by-line` is very convenient syntax-wise, it fails to produce
+ more sophisticated bullet lists, enumerations or term lists.
+ For example, non-tight lists are out of reach.
+
+ For that reason, there are `#list-one-by-one`, `#enum-one-by-one`, and
+ `#terms-one-by-one`, respectively.
+ #example[
+ #grid(
+ columns: (1fr, 1fr),
+ gutter: 1em,
+ ```typ
+ #enum-one-by-one(start: 2, tight: false, numbering: "i)")[first][second][third]
+ ```,
+ enum-one-by-one(start: 2, tight: false, numbering: "i)")[first][second][third]
+ )
+ ]
+
+ Note that, for technical reasons, the bullet points, numbers, or terms are
+ never covered.
+
+ `start` is again optional and defaults to `1`.
+]
+
+
+/*
#slide(title: "Different ways of covering content")[
When content is covered, it is completely invisible by default.
@@ -272,6 +294,7 @@
- `#one-by-one(...)[...][...]`
- `#line-by-line(...)[...][...]`
]
+*/
#new-section-slide("Themes")
@@ -293,11 +316,6 @@
It's very minimalist and helps the audience focus on an important point.
]
-#slide(
- title: [The `clean` theme also makes multiple colums very easy!],
- lorem(20), lorem(30), lorem(25)
-)
-
#slide(title: "Your own theme?")[
If you want to create your own design for slides, you can define custom
themes!
@@ -306,11 +324,32 @@
explains how to do so.
]
-#new-section-slide("Typst features")
+#new-section-slide("Utilities")
-#slide(title: "Use Typst!")[
- Typst gives us so many cool things #footnote[For example footnotes!].
- Use them!
+#slide(title: [The `utils` module])[
+ Polylux ships a `utils` module with solutions for common tasks in slide
+ building.
+]
+
+#slide(title: [Fit to height])[
+ You can scale content such that it has a certain height using
+ `#fit-to-height(height, content)`:
+
+ #fit-to-height(2.5cm)[Height is `2.5cm`]
+]
+
+#slide(title: "Fill remaining space")[
+ This function also allows you to fill the remaining space by using fractions
+ as heights, i.e. `fit-to-height(1fr)[...]`:
+
+ #fit-to-height(1fr)[Wow!]
+]
+
+#slide(title: "Side by side content")[
+ Often you want to put different content next to each other.
+ We have the function `#side-by-side` for that:
+
+ #side-by-side(lorem(10), lorem(20), lorem(15))
]
#slide(title: "Outline")[
@@ -318,6 +357,13 @@
#polylux-outline(padding: 1em, enum-args: (tight: false))
]
+#new-section-slide("Typst features")
+
+#slide(title: "Use Typst!")[
+ Typst gives us so many cool things #footnote[For example footnotes!].
+ Use them!
+]
+
#slide(title: "Bibliography")[
Let us cite something so we can have a bibliography: @A @B @C
#bibliography(title: none, "literature.bib")
diff --git a/examples/gauss.pdfpc b/examples/gauss.pdfpc
new file mode 100644
index 0000000..a4a68b5
--- /dev/null
+++ b/examples/gauss.pdfpc
@@ -0,0 +1 @@
+{"pdfpcFormat":2,"duration":15,"startTime":"08:15","disableMarkdown":false,"noteFontSize":5,"defaultTransition":"push:0.3:180:horizontal:outward","endSlide":9,"savedSlide":3,"pages":[{"idx":0,"label":1,"overlay":0,"forcedOverlay":false,"hidden":false},{"idx":1,"label":2,"overlay":0,"forcedOverlay":false,"hidden":false},{"idx":2,"label":3,"overlay":0,"forcedOverlay":false,"hidden":false,"note":"Remember to explain Sigma notation!"},{"idx":3,"label":4,"overlay":0,"forcedOverlay":false,"hidden":false},{"idx":4,"label":5,"overlay":0,"forcedOverlay":false,"hidden":false},{"idx":5,"label":6,"overlay":0,"forcedOverlay":false,"hidden":true},{"idx":6,"label":7,"overlay":0,"forcedOverlay":false,"hidden":false,"note":"# How the last steps work\nWe use _basic algebra_ rules for the last steps."},{"idx":7,"label":7,"overlay":1,"forcedOverlay":true,"hidden":false,"note":"# How the last steps work\nWe use _basic algebra_ rules for the last steps."},{"idx":8,"label":7,"overlay":2,"forcedOverlay":true,"hidden":false,"note":"# How the last steps work\nWe use _basic algebra_ rules for the last steps."},{"idx":9,"label":7,"overlay":3,"forcedOverlay":true,"hidden":false,"note":"# How the last steps work\nWe use _basic algebra_ rules for the last steps."},{"idx":10,"label":7,"overlay":4,"forcedOverlay":true,"hidden":false,"note":"# How the last steps work\nWe use _basic algebra_ rules for the last steps."},{"idx":11,"label":8,"overlay":0,"forcedOverlay":false,"hidden":false},{"idx":12,"label":9,"overlay":0,"forcedOverlay":false,"hidden":false},{"idx":13,"label":10,"overlay":0,"forcedOverlay":false,"hidden":false},{"idx":14,"label":11,"overlay":0,"forcedOverlay":false,"hidden":false}]}
\ No newline at end of file
diff --git a/examples/gauss.typ b/examples/gauss.typ
index d4f70c4..724f456 100644
--- a/examples/gauss.typ
+++ b/examples/gauss.typ
@@ -9,6 +9,13 @@
footer: [Sum of natural numbers, CF Gauß],
)
+#pdfpc.config(
+ duration-minutes: 15,
+ start-time: datetime(hour: 8, minute: 15, second: 0),
+ note-font-size: 5,
+ default-transition: (type: "push", duration-seconds: 0.3),
+)
+
#title-slide(
authors: "Carl Friedrich Gauß",
title: [On a revolutionary way to \ sum up natural numbers],
@@ -22,6 +29,7 @@
Let $n in NN$.
We are interested in sums of the form
$ 1 + ... + n = sum_(i=1)^n i $
+ #pdfpc.speaker-note("Remember to explain Sigma notation!")
]
#slide(title: "The theorem")[
@@ -30,6 +38,7 @@
$ sum_(i=1)^n i = n(n+1)/2 $
Let's prove that!
+ #pdfpc.save-slide
]
#new-section-slide("Proof")
@@ -40,6 +49,8 @@
+ base case
+ induction hypothesis
+ induction step
+
+ #pdfpc.hidden-slide
]
#slide(title: "Proof")[
@@ -64,6 +75,11 @@
= ((k+1)(k+2))/2
#h(1em) checkmark$
]
+
+ #pdfpc.speaker-note(```md
+# How the last steps work
+We use _basic algebra_ rules for the last steps.
+ ```)
]
#focus-slide[
@@ -75,4 +91,11 @@
#slide(title: "That's it!")[
Now you know how to calculate those sums more quickly. Nice!
+
+ #pdfpc.end-slide
+]
+
+#slide(title: [Further references])[
+ If you want to learn more about this cool kind of math, you can start your
+ investigation here: https://en.wikipedia.org/wiki/Carl_Friedrich_Gauss
]
diff --git a/helpers.typ b/helpers.typ
deleted file mode 100644
index 9d2321e..0000000
--- a/helpers.typ
+++ /dev/null
@@ -1,31 +0,0 @@
-#import "logic.typ"
-
-#let sections-state = state("polylux-sections", ())
-#let register-section(name) = locate( loc => {
- sections-state.update(sections => {
- sections.push((body: name, loc: loc))
- sections
- })
-})
-#let current-section = locate( loc => {
- let sections = sections-state.at(loc)
- if sections.len() > 0 {
- sections.last().body
- } else {
- []
- }
-})
-#let polylux-outline(enum-args: (:), padding: 0pt) = locate( loc => {
- let sections = sections-state.final(loc)
- pad(padding, enum(
- ..enum-args,
- ..sections.map(section => link(section.loc, section.body))
- ))
-})
-
-#let polylux-progress(ratio-to-content) = locate( loc => {
- let ratio = logic.logical-slide.at(loc).first() / logic.logical-slide.final(loc).first()
- ratio-to-content(ratio)
-})
-
-#let last-slide-number = locate(loc => logic.logical-slide.final(loc).first())
\ No newline at end of file
diff --git a/logic.typ b/logic.typ
index 0d7a93d..1d2b86f 100644
--- a/logic.typ
+++ b/logic.typ
@@ -1,4 +1,5 @@
#let subslide = counter("subslide")
+#let pause-counter = counter("pause-counter")
#let logical-slide = counter("logical-slide")
#let repetitions = counter("repetitions")
#let handout-mode = state("handout-mode", false)
@@ -126,21 +127,71 @@
}
}
-#let alternatives(start: 1, position: bottom + left, ..children) = {
+#let alternatives-match(subslides-contents, position: bottom + left) = {
+ let subslides-contents = if type(subslides-contents) == "dictionary" {
+ subslides-contents.pairs()
+ } else {
+ subslides-contents
+ }
+
+ let subslides = subslides-contents.map(it => it.first())
+ let contents = subslides-contents.map(it => it.last())
style(styles => {
- let sizes = children.pos().map(c => measure(c, styles))
+ let sizes = contents.map(c => measure(c, styles))
let max-width = calc.max(..sizes.map(sz => sz.width))
let max-height = calc.max(..sizes.map(sz => sz.height))
- for (idx, child) in children.pos().enumerate() {
- only(start + idx, box(
+ for (subslides, content) in subslides-contents {
+ only(subslides, box(
width: max-width,
height: max-height,
- align(position, child)
+ align(position, content)
))
}
})
}
+#let alternatives(
+ start: 1,
+ repeat-last: false,
+ ..args
+) = {
+ let contents = args.pos()
+ let kwargs = args.named()
+ let subslides = range(start, start + contents.len())
+ if repeat-last {
+ subslides.last() = (beginning: subslides.last())
+ }
+ alternatives-match(subslides.zip(contents), ..kwargs)
+}
+
+#let alternatives-fn(
+ start: 1,
+ end: none,
+ count: none,
+ ..kwargs,
+ fn
+) = {
+ let end = if end == none {
+ if count == none {
+ panic("You must specify either end or count.")
+ } else {
+ start + count
+ }
+ } else {
+ end
+ }
+
+ let subslides = range(start, end)
+ let contents = subslides.map(fn)
+ alternatives-match(subslides.zip(contents), ..kwargs.named())
+}
+
+#let alternatives-cases(cases, fn, ..kwargs) = {
+ let idcs = range(cases.len())
+ let contents = idcs.map(fn)
+ alternatives-match(cases.zip(contents), ..kwargs.named())
+}
+
#let line-by-line(start: 1, mode: "invisible", body) = {
let items = if repr(body.func()) == "sequence" {
body.children
@@ -159,10 +210,59 @@
}
}
-#let pause(beginning, mode: "invisible") = body => {
- uncover((beginning: beginning), mode: mode, body)
+
+#let _items-one-by-one(fn, start: 1, mode: "invisible", ..args) = {
+ let kwargs = args.named()
+ let items = args.pos()
+ let covered-items = items.enumerate().map(
+ ((idx, item)) => uncover((beginning: idx + start), mode: mode, item)
+ )
+ fn(
+ ..kwargs,
+ ..covered-items
+ )
+}
+
+#let list-one-by-one(start: 1, mode: "invisible", ..args) = {
+ _items-one-by-one(list, start: start, mode: mode, ..args)
+}
+
+#let enum-one-by-one(start: 1, mode: "invisible", ..args) = {
+ _items-one-by-one(enum, start: start, mode: mode, ..args)
+}
+
+#let terms-one-by-one(start: 1, mode: "invisible", ..args) = {
+ let kwargs = args.named()
+ let items = args.pos()
+ let covered-items = items.enumerate().map(
+ ((idx, item)) => terms.item(
+ item.term,
+ uncover((beginning: idx + start), mode: mode, item.description)
+ )
+ )
+ terms(
+ ..kwargs,
+ ..covered-items
+ )
+}
+
+#let pause = {
+ pause-counter.step()
+ locate( loc => {
+ repetitions.update(rep => calc.max(rep, pause-counter.at(loc).first() + 1))
+ })
}
+#let paused-content(body) = locate( loc => {
+ let current-subslide = subslide.at(loc).first()
+ let current-pause-counter = pause-counter.at(loc).first()
+
+ if current-subslide > current-pause-counter {
+ body
+ } else {
+ hide(body)
+ }
+})
#let polylux-slide(max-repetitions: 10, body) = {
locate( loc => {
@@ -174,13 +274,34 @@
subslide.update(1)
repetitions.update(1)
+ show text: paused-content
+ show smartquote: paused-content
+ show box: paused-content
+ show block: paused-content
+ show path: paused-content
+ show rect: paused-content
+ show square: paused-content
+ show circle: paused-content
+ show ellipse: paused-content
+ show line: paused-content
+ show polygon: paused-content
+ show image: paused-content
+
for _ in range(max-repetitions) {
+ pause-counter.update(0)
locate( loc => {
let curr-subslide = subslide.at(loc).first()
if curr-subslide <= repetitions.at(loc).first() {
if curr-subslide > 1 { pagebreak(weak: true) }
set heading(outlined: false) if curr-subslide > 1
+ [
+ #metadata((t: "NewSlide"))
+ #metadata((t: "Idx", v: counter(page).at(loc).first() - 1))
+ #metadata((t: "Overlay", v: curr-subslide - 1))
+ #metadata((t: "LogicalSlide", v: logical-slide.at(loc).first()))
+ ]
+
body
}
})
diff --git a/pdfpc-extractor/.gitignore b/pdfpc-extractor/.gitignore
new file mode 100644
index 0000000..e420ee4
--- /dev/null
+++ b/pdfpc-extractor/.gitignore
@@ -0,0 +1 @@
+target/*
diff --git a/pdfpc-extractor/Cargo.lock b/pdfpc-extractor/Cargo.lock
new file mode 100644
index 0000000..e467692
--- /dev/null
+++ b/pdfpc-extractor/Cargo.lock
@@ -0,0 +1,474 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "backtrace-ext"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
+dependencies = [
+ "backtrace",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "errno"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "is-terminal"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+dependencies = [
+ "hermit-abi",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "is_ci"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "miette"
+version = "5.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
+dependencies = [
+ "backtrace",
+ "backtrace-ext",
+ "is-terminal",
+ "miette-derive",
+ "once_cell",
+ "owo-colors",
+ "supports-color",
+ "supports-hyperlinks",
+ "supports-unicode",
+ "terminal_size",
+ "textwrap",
+ "thiserror",
+ "unicode-width",
+]
+
+[[package]]
+name = "miette-derive"
+version = "5.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "object"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "owo-colors"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
+
+[[package]]
+name = "polylux2pdfpc"
+version = "0.1.0"
+dependencies = [
+ "miette",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustix"
+version = "0.38.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "serde"
+version = "1.0.186"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.186"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.105"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "smawk"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
+
+[[package]]
+name = "supports-color"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354"
+dependencies = [
+ "is-terminal",
+ "is_ci",
+]
+
+[[package]]
+name = "supports-hyperlinks"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
+dependencies = [
+ "is-terminal",
+]
+
+[[package]]
+name = "supports-unicode"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7"
+dependencies = [
+ "is-terminal",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "terminal_size"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d"
+dependencies = [
+ "smawk",
+ "unicode-linebreak",
+ "unicode-width",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "unicode-linebreak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
diff --git a/pdfpc-extractor/Cargo.toml b/pdfpc-extractor/Cargo.toml
new file mode 100644
index 0000000..651fe76
--- /dev/null
+++ b/pdfpc-extractor/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "polylux2pdfpc"
+license = "MIT"
+description = "A tool to make pdfpc interpret slides created by polylux correctly"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+miette = { version = "5.10.0", features = ["fancy"] }
+serde = { version = "1.0.186", features = ["derive"] }
+serde_json = "1.0.105"
diff --git a/pdfpc-extractor/src/main.rs b/pdfpc-extractor/src/main.rs
new file mode 100644
index 0000000..2ad1d30
--- /dev/null
+++ b/pdfpc-extractor/src/main.rs
@@ -0,0 +1,290 @@
+use miette::{miette, IntoDiagnostic, WrapErr};
+use serde::{Deserialize, Serialize};
+use std::{convert::TryFrom, process::Command};
+
+#[derive(Deserialize)]
+#[serde(transparent)]
+struct TypstQueryOutput(Vec);
+
+#[derive(Debug, Deserialize)]
+#[serde(tag = "t", content = "v")]
+enum QueryItem {
+ Duration(u32),
+ StartTime(String),
+ EndTime(String),
+ LastMinutes(u32),
+ DisableMarkdown(bool),
+ NoteFontSize(u32),
+ DefaultTransition(String),
+
+ NewSlide,
+ Idx(u32),
+ LogicalSlide(u32),
+ Overlay(u32),
+ Note(String),
+ EndSlide,
+ SaveSlide,
+ HiddenSlide,
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+struct Page {
+ idx: u32,
+ #[serde(rename = "label")]
+ logical_slide: u32,
+ overlay: u32,
+ forced_overlay: bool,
+ hidden: bool,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ note: Option,
+ #[serde(skip)]
+ end: bool,
+ #[serde(skip)]
+ saved: bool,
+}
+
+impl<'a> TryFrom<&'a [QueryItem]> for Page {
+ type Error = miette::Report;
+
+ fn try_from(items: &'a [QueryItem]) -> miette::Result {
+ use QueryItem::*;
+ Ok(Page {
+ idx: items
+ .iter()
+ .find_map(|item| if let Idx(idx) = item { Some(idx) } else { None })
+ .cloned()
+ .ok_or_else(|| miette!("Page has no idx."))?,
+ logical_slide: items
+ .iter()
+ .find_map(|item| {
+ if let LogicalSlide(logical_slide) = item {
+ Some(logical_slide)
+ } else {
+ None
+ }
+ })
+ .cloned()
+ .ok_or_else(|| miette!("Page has no label."))?,
+ overlay: items
+ .iter()
+ .find_map(|item| {
+ if let Overlay(overlay) = item {
+ Some(overlay)
+ } else {
+ None
+ }
+ })
+ .cloned()
+ .ok_or_else(|| miette!("Page has no overlay."))?,
+ note: items
+ .iter()
+ .find_map(|item| {
+ if let Note(note) = item {
+ Some(note)
+ } else {
+ None
+ }
+ })
+ .cloned(),
+ forced_overlay: items.iter().any(|item| match item {
+ &Overlay(overlay) if overlay > 0 => true,
+ _ => false,
+ }),
+ hidden: items.iter().any(|item| matches!(item, HiddenSlide)),
+ end: items.iter().any(|item| matches!(item, EndSlide)),
+ saved: items.iter().any(|item| matches!(item, SaveSlide)),
+ })
+ }
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+struct PdfpcConfig {
+ pdfpc_format: u32,
+ /// Duration in minutes
+ #[serde(skip_serializing_if = "Option::is_none")]
+ duration: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ start_time: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ end_time: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ last_minutes: Option,
+ disable_markdown: bool,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ note_font_size: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ default_transition: Option,
+}
+
+impl<'a> TryFrom<&'a [QueryItem]> for PdfpcConfig {
+ type Error = miette::Report;
+
+ fn try_from(items: &'a [QueryItem]) -> miette::Result {
+ use QueryItem::*;
+ Ok(PdfpcConfig {
+ pdfpc_format: 2,
+ duration: items
+ .iter()
+ .find_map(|item| {
+ if let Duration(duration) = item {
+ Some(duration)
+ } else {
+ None
+ }
+ })
+ .cloned(),
+ start_time: items
+ .iter()
+ .find_map(|item| {
+ if let StartTime(start_time) = item {
+ Some(start_time)
+ } else {
+ None
+ }
+ })
+ .cloned(),
+ end_time: items
+ .iter()
+ .find_map(|item| {
+ if let EndTime(end_time) = item {
+ Some(end_time)
+ } else {
+ None
+ }
+ })
+ .cloned(),
+ last_minutes: items
+ .iter()
+ .find_map(|item| {
+ if let LastMinutes(last_minutes) = item {
+ Some(last_minutes)
+ } else {
+ None
+ }
+ })
+ .cloned(),
+ note_font_size: items
+ .iter()
+ .find_map(|item| {
+ if let NoteFontSize(note_font_size) = item {
+ Some(note_font_size)
+ } else {
+ None
+ }
+ })
+ .cloned(),
+ default_transition: items
+ .iter()
+ .find_map(|item| {
+ if let DefaultTransition(default_transition) = item {
+ Some(default_transition)
+ } else {
+ None
+ }
+ })
+ .cloned(),
+ disable_markdown: items
+ .iter()
+ .any(|item| matches!(item, DisableMarkdown(true))),
+ })
+ }
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+struct Pdfpc {
+ #[serde(flatten)]
+ config: PdfpcConfig,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ end_slide: Option,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ saved_slide: Option,
+ pages: Vec,
+}
+
+impl TryFrom for Pdfpc {
+ type Error = miette::Report;
+
+ fn try_from(output: TypstQueryOutput) -> miette::Result {
+ let mut split_output = output.0.split(|item| matches!(item, QueryItem::NewSlide));
+ let config_items = split_output
+ .next()
+ .ok_or_else(|| miette!("There are no slides."))?;
+ let config = PdfpcConfig::try_from(config_items)
+ .wrap_err("Failed to aggregate pdfpc configuration data.")?;
+ let pages = split_output
+ .map(Page::try_from)
+ .collect::, _>>()
+ .wrap_err("Failed to aggregate page information.")?;
+
+ Ok(Pdfpc {
+ config,
+ end_slide: pages.iter().find_map(|page| {
+ if page.end {
+ Some(page.logical_slide - 1)
+ } else {
+ None
+ }
+ }),
+ saved_slide: pages.iter().find_map(|page| {
+ if page.saved {
+ Some(page.logical_slide - 1)
+ } else {
+ None
+ }
+ }),
+ pages,
+ })
+ }
+}
+
+fn main() -> miette::Result<()> {
+ let args: Vec<_> = std::env::args().skip(1).collect();
+ let filename = args
+ .iter()
+ .find(|arg| arg.ends_with(".typ"))
+ .cloned()
+ .ok_or_else(|| miette!("No .typ file provided."))?;
+
+ // dbg!(filename);
+
+ let query_output = Command::new("typst")
+ .arg("query")
+ .args(args)
+ .arg("--field")
+ .arg("value")
+ .arg("")
+ .output()
+ .into_diagnostic()
+ .wrap_err("typst query failed.")?;
+ let query_str = String::from_utf8(query_output.stdout)
+ .into_diagnostic()
+ .wrap_err("typst query produced invalid UTF-8 data.")?;
+
+ if query_str.is_empty() {
+ let query_errstr = String::from_utf8(query_output.stderr)
+ .into_diagnostic()
+ .wrap_err("typst query produced invalid UTF-8 on stderr.")?;
+ miette::bail!(miette::diagnostic!(
+ help = query_errstr,
+ "typst query did not produce any output."
+ ))
+ }
+
+ let query: TypstQueryOutput = serde_json::from_str(&query_str)
+ .into_diagnostic()
+ .wrap_err("Failed to parse JSON produced by typst query.")?;
+
+ let pdfpc = Pdfpc::try_from(query).wrap_err("Failed to construct pdfpc data")?;
+
+ let output = serde_json::to_string(&pdfpc)
+ .into_diagnostic()
+ .wrap_err("Failed to create pdfpc JSON.")?;
+ let outfile = std::path::Path::new(&filename).with_extension("pdfpc");
+ std::fs::write(outfile, output)
+ .into_diagnostic()
+ .wrap_err("Failed to write pdfpc JSON to file.")?;
+ Ok(())
+}
diff --git a/polylux.typ b/polylux.typ
index 3caf30d..25cfea1 100644
--- a/polylux.typ
+++ b/polylux.typ
@@ -1,3 +1,5 @@
#import "themes/themes.typ"
-#import "logic.typ": polylux-slide, uncover, only, alternatives, one-by-one, line-by-line, pause, enable-handout-mode
-#import "helpers.typ": polylux-outline
+#import "logic.typ"
+#import "logic.typ": polylux-slide, uncover, only, alternatives, alternatives-match, alternatives-fn, alternatives-cases, one-by-one, line-by-line, list-one-by-one, enum-one-by-one, terms-one-by-one, pause, enable-handout-mode
+#import "utils/utils.typ"
+#import "utils/utils.typ": polylux-outline, fit-to-height, side-by-side, pdfpc
diff --git a/scripts/extract-package.fish b/scripts/extract-package.fish
index fd59ae8..0d73182 100644
--- a/scripts/extract-package.fish
+++ b/scripts/extract-package.fish
@@ -1,4 +1,9 @@
function extract-package
+ if test (git branch --show-current) != "release"
+ echo "You are not on the release branch!"
+ return 1
+ end
+
set target $argv[1]
pwd
mkdir -p $target
@@ -7,8 +12,9 @@ function extract-package
cp typst.toml $target
cp polylux.typ $target
cp logic.typ $target
- cp helpers.typ $target
mkdir -p $target/themes
cp themes/* $target/themes
+ mkdir -p $target/utils
+ cp utils/* $target/utils
echo "Done"
-end
\ No newline at end of file
+end
diff --git a/scripts/generate-previews.jl b/scripts/generate-previews.jl
index 84b7f0b..51e1331 100644
--- a/scripts/generate-previews.jl
+++ b/scripts/generate-previews.jl
@@ -79,7 +79,7 @@ function generate_previews(items)
mktempdir() do tmp
typst_output = joinpath(tmp, "img-{n}.png")
typst_input = item.input
- `typst --root . compile $typst_input $typst_output` |> run
+ `typst compile --root . $typst_input $typst_output` |> run
imgs = readdir(tmp, join = true) .|> load
plt = montage(imgs, item.label)
savefig(plt, item.output)
@@ -108,6 +108,9 @@ dynamic = "book/src/dynamic"
# ╔═╡ b7923561-c79b-4443-99c2-4306589313d6
themes = "book/src/themes"
+# ╔═╡ 98d47b63-9744-4f68-aff9-6b80a1bd2212
+utils = "book/src/utils"
+
# ╔═╡ c3934766-e918-456d-81ea-de1e4726d3b6
gallery = joinpath(themes, "gallery")
@@ -125,10 +128,17 @@ generate_previews([
typ2png(path = dynamic, file = "one-by-one", label = "subslide "),
typ2png(path = dynamic, file = "one-by-one-start", label = "subslide "),
typ2png(path = dynamic, file = "line-by-line", label = "subslide "),
+ typ2png(path = dynamic, file = "list-one-by-one", label = "subslide "),
+ typ2png(path = dynamic, file = "enum-one-by-one", label = "subslide "),
+ typ2png(path = dynamic, file = "terms-one-by-one", label = "subslide "),
typ2png(path = dynamic, file = "pause", label = "subslide "),
typ2png(path = dynamic, file = "poor-alternatives", label = "subslide "),
typ2png(path = dynamic, file = "alternatives", label = "subslide "),
+ typ2png(path = dynamic, file = "alternatives-repeat-last", label = "subslide "),
typ2png(path = dynamic, file = "alternatives-position", label = "subslide "),
+ typ2png(path = dynamic, file = "alternatives-match", label = "subslide "),
+ typ2png(path = dynamic, file = "alternatives-cases", label = "subslide "),
+ typ2png(path = dynamic, file = "alternatives-fn", label = "subslide "),
typ2png(path = dynamic, file = "cover", label = "subslide "),
typ2png(path = dynamic, file = "handout", label = "subslide "),
typ2png(path = themes, file = "science-slam"),
@@ -137,6 +147,10 @@ generate_previews([
typ2png(path = gallery, file = "metropolis"),
typ2png(path = gallery, file = "university"),
typ2png(path = gallery, file = "bipartite"),
+ typ2png(path = utils, file = "side-by-side"),
+ typ2png(path = utils, file = "side-by-side-kwargs"),
+ typ2png(path = utils, file = "fill-remaining"),
+ typ2png(path = utils, file = "fit-to-height-width"),
])
# ╔═╡ 00000000-0000-0000-0000-000000000001
@@ -1664,6 +1678,7 @@ version = "1.4.1+0"
# ╠═59a40cbd-5ad7-4c22-b676-e85bbf9ee918
# ╠═09396295-f40a-4bb1-b261-81f784d93bc2
# ╠═b7923561-c79b-4443-99c2-4306589313d6
+# ╠═98d47b63-9744-4f68-aff9-6b80a1bd2212
# ╠═c3934766-e918-456d-81ea-de1e4726d3b6
# ╠═aa8102d8-029f-4a94-bce4-a7f362b64e4c
# ╟─00000000-0000-0000-0000-000000000001
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..a136337
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1 @@
+*.pdf
diff --git a/tests/alternatives.typ b/tests/alternatives.typ
new file mode 100644
index 0000000..39febea
--- /dev/null
+++ b/tests/alternatives.typ
@@ -0,0 +1,41 @@
+#import "../polylux.typ": *
+
+#set page(paper: "presentation-16-9")
+
+#polylux-slide[
+ == Test that `repeat-last` works
+
+ #alternatives[abc ][def ][ghi ]
+
+ #alternatives(repeat-last: true)[jkl ][mno ][this stays ]
+
+ #uncover(5)[You can go now.]
+]
+
+#polylux-slide[
+ == Test that `alternatives-match` works
+
+ #alternatives-match(position: center, (
+ "-2": [beginning],
+ "3, 5": [main part],
+ "4": [short break],
+ "6-" : [end]
+ ))
+
+ #uncover("1-8")[I am always here, for technical reasons.]
+]
+
+#polylux-slide[
+ == Test that `alternatives-cases` works
+
+ #alternatives-cases(("1,3,4", "2,5", "6"), case => {
+ set text(fill: lime) if case == 1
+ lorem(10)
+ })
+]
+
+#polylux-slide[
+ == Test that `alternatives-fn` works
+
+ #alternatives-fn(count: 5, subslide => numbering("(i)", subslide))
+]
diff --git a/tests/pause.typ b/tests/pause.typ
new file mode 100644
index 0000000..a6edd60
--- /dev/null
+++ b/tests/pause.typ
@@ -0,0 +1,86 @@
+#import "../polylux.typ": *
+
+#set page(paper: "presentation-16-9")
+
+#polylux-slide[
+ == Text like content
+
+ Hello
+ #pause
+ $a + b$
+ #pause
+ $ integral f(x) dif x $
+ #pause
+
+ - *item1*
+ - _item2_
+ - `item3`
+
+ #pause
+
+ + #underline[item1]
+ + #strike[item2]
+ + #overline[item3]
+
+ #pause
+
+ / def1: abc
+ / def2: ghi
+
+ #pause
+
+ #box(stroke: 2pt + aqua, inset: 2pt)[boxed!]
+
+ #pause
+
+ #block(stroke: 2pt + lime, inset: 2pt)[blocked!]
+]
+
+#polylux-slide[
+ == Inside grid
+
+ #grid(columns: 4 * (1fr,))[
+ abc
+ #pause
+ ][
+ def
+ #pause
+ ][
+ ghi
+ #pause
+ ][
+ jkl
+ ]
+]
+
+#polylux-slide[
+ == Visuals
+
+ #pause
+
+ // Fails to be hidden as of Typst 0.7.0
+ #path(
+ fill: teal.lighten(50%), stroke: teal, closed: true,
+ (0cm, 0cm), (1cm, 0cm), (1cm, 1cm)
+ )
+
+ #rect()
+
+ #square()
+
+ #circle()
+
+ #ellipse()
+
+ // Fails to be hidden as of Typst 0.7.0
+ #line()
+
+ // Fails to be hidden as of Typst 0.7.0
+ #polygon(
+ fill: teal.lighten(50%), stroke: teal,
+ (0cm, 0cm), (1cm, 0cm), (1cm, 1cm)
+ )
+
+ #image("../assets/logo.png", width: 3em)
+]
+
diff --git a/themes/clean.typ b/themes/clean.typ
index 60a59d8..a37905c 100644
--- a/themes/clean.typ
+++ b/themes/clean.typ
@@ -2,7 +2,7 @@
// https://github.com/MarkBlyth
#import "../logic.typ"
-#import "../helpers.typ"
+#import "../utils/utils.typ"
#let clean-footer = state("clean-footer", [])
#let clean-short-title = state("clean-short-title", none)
@@ -104,7 +104,7 @@
logic.polylux-slide(content)
}
-#let slide(title: none, columns: none, gutter: none, ..bodies) = {
+#let slide(title: none, body) = {
let header = align(top, locate( loc => {
let color = clean-color.at(loc)
let logo = clean-logo.at(loc)
@@ -124,10 +124,10 @@
align(horizon + right, grid(
columns: 1, rows: 1em, gutter: .5em,
short-title,
- helpers.current-section
+ utils.current-section
))
} else {
- align(horizon + right, helpers.current-section)
+ align(horizon + right, utils.current-section)
}
)
}))
@@ -153,14 +153,7 @@
header-ascent: 1.5em,
)
- let bodies = bodies.pos()
- let gutter = if gutter == none { 1em } else { gutter }
- let columns = if columns == none { (1fr,) * bodies.len() } else { columns }
- if columns.len() != bodies.len() {
- panic("number of columns must match number of content arguments")
- }
-
- let body = pad(x: .0em, y: .5em, grid(columns: columns, gutter: gutter, ..bodies))
+ let body = pad(x: .0em, y: .5em, body)
let content = {
@@ -176,7 +169,9 @@
#let focus-slide(background: teal, foreground: white, body) = {
set page(fill: background, margin: 2em)
set text(fill: foreground, size: 1.5em)
- logic.polylux-slide(align(horizon, body))
+ let content = { v(.1fr); body; v(.1fr) }
+ // logic.polylux-slide(align(horizon, body))
+ logic.polylux-slide(content)
}
#let new-section-slide(name) = {
@@ -187,7 +182,7 @@
show: block.with(stroke: ( bottom: 1mm + color ), inset: 1em,)
set text(size: 1.5em)
strong(name)
- helpers.register-section(name)
+ utils.register-section(name)
})
logic.polylux-slide(content)
}
diff --git a/themes/metropolis.typ b/themes/metropolis.typ
index 95aa7e9..cb1a7f2 100644
--- a/themes/metropolis.typ
+++ b/themes/metropolis.typ
@@ -8,7 +8,7 @@
// #set par(justify: true)
#import "../logic.typ"
-#import "../helpers.typ"
+#import "../utils/utils.typ"
#let m-dark-teal = rgb("#23373b")
#let m-light-brown = rgb("#eb811b")
@@ -25,7 +25,7 @@
breakable: false
)
-#let m-progress-bar = helpers.polylux-progress( ratio => {
+#let m-progress-bar = utils.polylux-progress( ratio => {
grid(
columns: (ratio * 100%, 1fr),
m-cell(fill: m-light-brown),
@@ -124,7 +124,7 @@
#let new-section-slide(name) = {
let content = {
- helpers.register-section(name)
+ utils.register-section(name)
set align(horizon)
show: pad.with(20%)
set text(size: 1.5em)
@@ -142,4 +142,4 @@
#let alert = text.with(fill: m-light-brown)
-#let metropolis-outline = helpers.polylux-outline(enum-args: (tight: false,))
+#let metropolis-outline = utils.polylux-outline(enum-args: (tight: false,))
diff --git a/themes/university.typ b/themes/university.typ
index 323f506..f78b2fe 100644
--- a/themes/university.typ
+++ b/themes/university.typ
@@ -1,5 +1,5 @@
#import "../logic.typ"
-#import "../helpers.typ"
+#import "../utils/utils.typ"
// University theme
//
@@ -97,28 +97,20 @@
#let slide(
title: none,
- columns: none,
- gutter: none,
header: none,
footer: none,
new-section: none,
- ..bodies
+ body
) = {
- let bodies = bodies.pos()
- let gutter = if gutter == none { 1em } else { gutter }
- let columns = if columns == none { (1fr,) * bodies.len() } else { columns }
- if columns.len() != bodies.len() {
- panic("number of columns must match number of content arguments")
- }
- let body = pad(x: 2em, y: .5em, grid(columns: columns, gutter: gutter, ..bodies))
+ let body = pad(x: 2em, y: .5em, body)
let progress-barline = locate( loc => {
if uni-progress-bar.at(loc) {
let cell = block.with( width: 100%, height: 100%, above: 0pt, below: 0pt, breakable: false )
let colors = uni-colors.at(loc)
- helpers.polylux-progress( ratio => {
+ utils.polylux-progress( ratio => {
grid(
rows: 2pt, columns: (ratio * 100%, 1fr),
cell(fill: colors.a),
@@ -133,14 +125,14 @@
header
} else if title != none {
if new-section != none {
- helpers.register-section(new-section)
+ utils.register-section(new-section)
}
locate( loc => {
let colors = uni-colors.at(loc)
block(fill: colors.c, inset: (x: .5em), grid(
columns: (60%, 40%),
align(top + left, heading(level: 2, text(fill: colors.a, title))),
- align(top + right, text(fill: colors.a.lighten(65%), helpers.current-section))
+ align(top + right, text(fill: colors.a.lighten(65%), utils.current-section))
))
})
} else { [] }
@@ -171,7 +163,7 @@
cell(fill: colors.a, uni-short-author.display()),
cell(uni-short-title.display()),
cell(uni-short-date.display()),
- cell(logic.logical-slide.display() + [~/~] + helpers.last-slide-number)
+ cell(logic.logical-slide.display() + [~/~] + utils.last-slide-number)
)
})
}
@@ -190,7 +182,7 @@
}
#let focus-slide(background-color: none, background-img: none, body) = {
- let background-color = if background-img == none and background-colour == none {
+ let background-color = if background-img == none and background-color == none {
rgb("#0C6291")
} else {
background-color
diff --git a/utils/pdfpc.typ b/utils/pdfpc.typ
new file mode 100644
index 0000000..27d9e6c
--- /dev/null
+++ b/utils/pdfpc.typ
@@ -0,0 +1,94 @@
+#let speaker-note(text) = {
+ let text = if type(text) == "string" {
+ text
+ } else if type(text) == "content" and text.func() == raw {
+ text.text.trim()
+ } else {
+ panic("A note must either be a string or a raw block")
+ }
+ [ #metadata((t: "Note", v: text)) ]
+}
+
+#let end-slide = [
+ #metadata((t: "EndSlide"))
+]
+
+#let save-slide = [
+ #metadata((t: "SaveSlide"))
+]
+
+#let hidden-slide = [
+ #metadata((t: "HiddenSlide"))
+]
+
+#let config(
+ duration-minutes: none,
+ start-time: none,
+ end-time: none,
+ last-minutes: none,
+ note-font-size: none,
+ disable-markdown: false,
+ default-transition: none,
+) = {
+ if duration-minutes != none {
+ [ #metadata((t: "Duration", v: duration-minutes)) ]
+ }
+
+ let _time-config(time, msg-name, tag-name) = {
+ let time = if type(time) == "datetime" {
+ time.display("[hour padding:zero repr:24]:[minute padding:zero]")
+ } else if type(time) == "string" {
+ time
+ } else {
+ panic(msg-name + " must be either a datetime or a string in the HH:MM format.")
+ }
+
+ [ #metadata((t: tag-name, v: time)) ]
+ }
+
+ if start-time != none {
+ _time-config(start-time, "Start time", "StartTime")
+ }
+
+ if end-time != none {
+ _time-config(end-time, "End time", "EndTime")
+ }
+
+ if last-minutes != none {
+ [ #metadata((t: "LastMinutes", v: last_minutes)) ]
+ }
+
+ if note-font-size != none {
+ [ #metadata((t: "NoteFontSize", v: note-font-size)) ]
+ }
+
+ [ #metadata((t: "DisableMarkdown", v: disable-markdown)) ]
+
+ if default-transition != none {
+ let dir-to-angle(dir) = if dir == ltr {
+ "0"
+ } else if dir == rtl {
+ "180"
+ } else if dir == ttb {
+ "90"
+ } else if dir == btt {
+ "270"
+ } else {
+ panic("angle must be a direction (ltr, rtl, ttb, or btt)")
+ }
+
+ let transition-str = (
+ default-transition.at("type", default: "replace")
+ + ":" +
+ str(default-transition.at("duration-seconds", default: 1))
+ + ":" +
+ dir-to-angle(default-transition.at("angle", default: rtl))
+ + ":" +
+ default-transition.at("alignment", default: "horizontal")
+ + ":" +
+ default-transition.at("direction", default: "outward")
+ )
+
+ [ #metadata((t: "DefaultTransition", v: transition-str)) ]
+ }
+}
diff --git a/utils/utils.typ b/utils/utils.typ
new file mode 100644
index 0000000..ba2690b
--- /dev/null
+++ b/utils/utils.typ
@@ -0,0 +1,142 @@
+#import "../logic.typ"
+
+#import "pdfpc.typ"
+
+// SECTIONS
+
+#let sections-state = state("polylux-sections", ())
+#let register-section(name) = locate( loc => {
+ sections-state.update(sections => {
+ sections.push((body: name, loc: loc))
+ sections
+ })
+})
+
+#let current-section = locate( loc => {
+ let sections = sections-state.at(loc)
+ if sections.len() > 0 {
+ sections.last().body
+ } else {
+ []
+ }
+})
+
+#let polylux-outline(enum-args: (:), padding: 0pt) = locate( loc => {
+ let sections = sections-state.final(loc)
+ pad(padding, enum(
+ ..enum-args,
+ ..sections.map(section => link(section.loc, section.body))
+ ))
+})
+
+
+// PROGRESS
+
+#let polylux-progress(ratio-to-content) = locate( loc => {
+ let ratio = logic.logical-slide.at(loc).first() / logic.logical-slide.final(loc).first()
+ ratio-to-content(ratio)
+})
+
+#let last-slide-number = locate(loc => logic.logical-slide.final(loc).first())
+
+
+// HEIGHT FITTING
+
+#let _size-to-pt(size, styles, container-dimension) = {
+ let to-convert = size
+ if type(size) == "ratio" {
+ to-convert = container-dimension * size
+ }
+ measure(v(to-convert), styles).height
+}
+
+#let _limit-content-width(width: none, body, container-size, styles) = {
+ let mutable-width = width
+ if width == none {
+ mutable-width = calc.min(container-size.width, measure(body, styles).width)
+ } else {
+ mutable-width = _size-to-pt(width, styles, container-size.width)
+ }
+ box(width: mutable-width, body)
+}
+
+#let fit-to-height(height, width: none, prescale-width: none, body) = {
+ // Place two labels with the requested vertical separation to be able to
+ // measure their vertical distance in pt.
+ // Using this approach instead of using `measure` allows us to accept fractions
+ // like `1fr` as well.
+ // The label must be attached to content, so we use a show rule that doesn't
+ // display anything as the anchor.
+ let before-label = label("polylux-fit-height-before")
+ let after-label = label("polylux-fit-height-after")
+ [
+ #show before-label: none
+ #show after-label: none
+ #v(1em)
+ hidden#before-label
+ #v(height)
+ hidden#after-label
+ ]
+
+ locate(loc => {
+ let before = query(selector(before-label).before(loc), loc)
+ let before-pos = before.last().location().position()
+ let after = query(selector(after-label).before(loc), loc)
+ let after-pos = after.last().location().position()
+
+ let available-height = after-pos.y - before-pos.y
+
+ style(styles => {
+ layout(container-size => {
+ // Helper function to more easily grab absolute units
+ let get-pts(body, w-or-h) = {
+ let dim = if w-or-h == "w" {container-size.width} else {container-size.height}
+ _size-to-pt(body, styles, dim)
+ }
+
+ // Provide a sensible initial width, which will define initial scale parameters.
+ // Note this is different from the post-scale width, which is a limiting factor
+ // on the allowable scaling ratio
+ let boxed-content = _limit-content-width(
+ width: prescale-width, body, container-size, styles
+ )
+
+ // post-scaling width
+ let mutable-width = width
+ if width == none {
+ mutable-width = container-size.width
+ }
+ mutable-width = get-pts(mutable-width, "w")
+
+ let size = measure(boxed-content, styles)
+ let h-ratio = available-height / size.height
+ let w-ratio = mutable-width / size.width
+ let ratio = calc.min(h-ratio, w-ratio) * 100%
+
+ let new-width = size.width * ratio
+ v(-available-height)
+ // If not boxed, the content can overflow to the next page even though it will fit.
+ // This is because scale doesn't update the layout information.
+ // Boxing in a container without clipping will inform typst that content
+ // will indeed fit in the remaining space
+ box(
+ width: new-width,
+ height: available-height,
+ scale(x: ratio, y: ratio, origin: top + left, boxed-content)
+ )
+ })
+ })
+ })
+}
+
+// SIDE BY SIDE
+
+#let side-by-side(columns: none, gutter: 1em, ..bodies) = {
+ let bodies = bodies.pos()
+ let columns = if columns == none { (1fr,) * bodies.len() } else { columns }
+ if columns.len() != bodies.len() {
+ panic("number of columns must match number of content arguments")
+ }
+
+ grid(columns: columns, gutter: gutter, ..bodies)
+}