How to Detect a Pagebreak?

I’m currently working on a slide template and I’m a bit stuck when it comes to the handling of the headings.

At each new page I would like to display the current heading, so that I don’t have to manually place it and let Typst do the layout. So far I haven’t found a way to detect a pagebreak though.

Is there a way at all?

Use page.header with hydra – Typst Universe?

For detecting long pagebreaks there is a hack: Skip headers and footers for skipped pages · Issue #2722 · typst/typst · GitHub. I don’t know how you would detect a regular pagebreak, let alone an implicit one. You only have here().page() or counter(page).get().

There are no callbacks that tell you there is a new page found, and you can add to it. You can manually call, and it will add 1 heading max because of on-demand checking.

I’d go with header or background.

There is one element that natively supports doing something special when being pagebroken by an (implicit) new page: tables (and grids), which use this to repeat the table header and/or footer. See more about that here: Allow adding content / configuring breakpoints of broken blocks · Issue #735 · typst/typst · GitHub

You can look into if it can be used, this can be used for “blocks that know when they are placed on more than one page”. It could however be quite limiting to force the user’s content to be placed inside a grid: for example, then regular pagebreak() is suddenly disallowed.

Thank you for the input.

@Andrew, I already looked into hydra and it really seems like the cleanest solution. I had some hopes that I missed a small note in the documentation but it seems like there is none.

@bluss thanks for the interesting idea. That sounds like a really interesting approach, maybe there’s something I can hack together from this ^^

Because it might be useful, here’s a different example but still using the repeat/count mechanism, taken from discord

#set page(margin: 1cm, width: 10cm, height: 6cm)
#let rep-count-header = counter("my-table-header-repeat")
#let repheader(first, cont) = {
  table.header(table.cell({
    rep-count-header.step()
    context {
      if rep-count-header.get().first() == 1 {
        first
      } else {
        cont
      }
    }
  }))
}

#let continuation-footer(cont, stroke: none) = {
  table.footer({
    table.cell(inset: 0pt, stroke: none, {
      rep-count-header.step()
      context {
      // show continuation footer if we have header continuations
      // and we're not on the last one.
      let s = rep-count-header.final()
      let t = rep-count-header.get()
      if t.first() < s.first() {
        set align(right)
        set block(stroke: (top: none, rest: stroke))
        show: pad.with(0.45em)  // manual inset
        cont
      } else {
        // Need to make the cell "invisible" / take no space when not used
        none
      }
    }})
  })
}

#show math.equation: set block(breakable: true)

#table(
  stroke: 0.2pt,
  repheader([Example], [Example (cont'd)]),
  {
  $
    [ sqrt(2)(cos pi/8 + i sin pi/8)]^12: \
    [ sqrt(2)(cos pi/8 + i sin pi/8)]^12 = \
    // just filler here
    = [ sqrt(2)(cos pi/8 + i sin pi/8)]^12 \
    = [ sqrt(2)(cos pi/8 + i sin pi/8)]^12 \
    = [ sqrt(2)(cos pi/8 + i sin pi/8)]^12 \
  $
  },
  continuation-footer(stroke: 0.2pt)[continued $->$]
)

Just because it might be simpler to extract the parts you need from a simpler example.

Awesome, thank you very much.

I also created a feature request. If you have any input, I’d be pleased, if you add it there :smiley:

See: Add an API to React to Page Breaks · Issue #6445 · typst/typst · GitHub

Hi @the_kief, thanks for your question! If you feel the response you got has sufficiently answered your question, be sure to give it a checkmark :ballot_box_with_check:. This will help others find the solution in the future. If something is missing, please let us know what so we can resolve your issue.

I also commented on your issue; if you feel that the proposed code there solves your issue, feel free to quote it here and then mark that reply as the solution.

Thanks!