Created
April 9, 2024 20:39
-
-
Save MultisampledNight/4745722857fce67e93e27de2953dfe3d to your computer and use it in GitHub Desktop.
heading → slide converter for typst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "@preview/polylux:0.3.1": * | |
#set text(font: "IBM Plex Sans") | |
#import themes.simple: * | |
#show: simple-theme | |
#enable-handout-mode(true) | |
// Splits the array `it` | |
// on each item where `check` returns `true`. | |
// These items are called "edge items". | |
// What is done with these edge items depends on `edge-action`: | |
// | |
// - `"discard"`: It is completely removed and not present anywhere. | |
// - `"isolate"`: It receives its whole own array to have fun in. | |
// - `"left"`: It becomes the end of the array before it. | |
// - `"right"`: It becomes the start of the next array. | |
// | |
// If it's none of these, the function panics. | |
// If it's unspecified, the default is `"discard"`. | |
#let split-by(it, check, edge-action: "discard") = { | |
if not ("discard", "isolate", "left", "right").contains(edge-action) { | |
panic("unknown edge action `" + edge-action + "`") | |
} | |
let result = ((),) | |
// edge action discard is handled by being not handled | |
// in which case the edge item is done nothing with, and just forgotten | |
for item in it { | |
let is-edge = check(item) | |
if is-edge { | |
if edge-action == "left" { | |
result.push(item) | |
} | |
result.push(()) | |
if edge-action == "isolate" { | |
result.last().push(item) | |
result.push(()) | |
} | |
} | |
if edge-action == "right" or not is-edge { | |
result.last().push(item) | |
} | |
} | |
if result.last().len() == 0 { | |
let _ = result.pop() | |
} | |
result | |
} | |
#let split-onto-slides(it) = { | |
// isolate the top-level headings | |
let sections = split-by( | |
it.children, | |
part => part.func() == heading and part.depth == 1, | |
edge-action: "isolate" | |
) | |
// then care about the rest and get each heading with its body | |
let slide-contents = sections | |
.map(section => split-by( | |
section, | |
// checking for != 1 as we don't want to further split the toplevel ones | |
part => part.func() == heading and part.depth != 1, | |
edge-action: "right", | |
)) | |
.join() | |
.map(slide => slide.join()) | |
// recombine them into proper slides | |
let front-slide = title-slide(slide-contents.first()) | |
let main-slides = slide-contents | |
.slice(1, -2) | |
.map(body => { | |
let kind = if body.func() == heading { | |
// most likely a toplevel heading -> 1st-level one | |
// those should be prominently centered | |
centered-slide | |
} else { | |
slide | |
} | |
kind(body) | |
}) | |
let final-slide = centered-slide( | |
slide-contents.slice(-2).join() | |
) | |
// .flatten() causes a repr() display for some reason | |
( | |
(front-slide,), | |
main-slides, | |
(final-slide,), | |
).join().join() | |
} | |
// this show rule should be after any other `show` and `set` rules | |
// since it transform the document quite drastically | |
// if it is styled via `set` afterwards, | |
// it can't get the `.children` array out of the `sequence` | |
#show: split-onto-slides | |
#text(size: 1.5em)[cursed `sequence` hacks] | |
by: a cat \ | |
(the content before the first heading is the title/first slide) | |
= why | |
== less repetition | |
#line-by-line[ | |
- i was bored by having an extra indent all the time | |
- i noticed that i structure my presentations very similar | |
- toplevel headings with depth 1 | |
- are the ones with just 1 `=` (equal sign) | |
- get their own full slide which allows me to transition nicely | |
- all other headings | |
- are the titles of slides | |
- usually a summary of what the slide wants to say | |
- also helps with rembering what to say when presenting | |
] | |
== the #strike[false]true reason | |
#line-by-line[ | |
- i was bored | |
- needed something to procastinate | |
- sowwy | |
] | |
= how | |
== failed: `query`ing `text` | |
#line-by-line[ | |
- "hmm i want to have an array of each heading and its text" | |
- "hmm lol i can put `text` into a `show` rule??" | |
- "but i can't `query` for `text`" | |
- "well i can, by labeling each text, but that'd mean" | |
- "i'd need to label + `show` rule every content type manually" | |
- "this would have a scary quadratic or worse runtime" | |
- "i'm bored with this problem let's do something else" | |
] | |
== this try: top-level `show` | |
#line-by-line[ | |
- "...unsatisfying. i started to do this. i want to see this working" | |
- "...how does a document look like from a show: ... perspective" | |
- "oh it's just a sequence" | |
- "i've hacked around with those" | |
- "behind `.children` they're just `array`" | |
- "guess i can split on the `array` + use functional programming?" | |
- "oh here we go, it works perfectly" | |
] | |
= where do we go from here | |
== absolutely nowhere | |
#line-by-line[ | |
- this thing is under the MIT license to be compatible with polylux | |
- i'd love to contribute this to polylux | |
- but i'm not sure if polylux needs this highly opinionated thing tbh | |
- maybe as part of a theme? | |
- feel free to use this in whatever project you want to tho uwu | |
- for looking at the internals, do this to above | |
```diff | |
-kind(body) | |
+kind(repr(body)) | |
``` | |
] | |
== how do i not type `#line-by-line` all the time | |
#line-by-line[ | |
- one could check if the toplevel is a sequence consisting only of | |
- `item` | |
- whitespace `content` | |
- if that's true, wrap the whole slide in `#line-by-line` | |
- i'm too lazy to actually impl that though | |
- and this document is far too long already, | |
i accidentally overwrote it and needed to rewrite it, | |
i should get back to my actual presentation lol | |
] | |
= thank you for listening | |
or not because you're most likely reading this \ | |
or you convinced me to actually hold a talk about this | |
the last 1st-level heading and its content will be the final slide |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment