How to extract pieces of content from another document?

hi everyone!! basically, i have a standalone textbook.typ document, which contains a bunch of instances of, let’s say, a problem() function, which can contain any arbitrary content.

then i have a separate document, workbook.typ, which is supposed to grab these problems from the other doc and intersperse them with more content. does anyone have any ideas on how to do this? here are some approaches that don’t work:

  • using query(): in order to query it, the entire content must be present in the final document, which we don’t want here. we can’t just include then hide() it because that still shows blank pages, or box(width: 0) it because that causes container errors.
  • using typst query and passing this via --input: this command serializes the content tree lossily (e.g. set rules get turned into styled(..)), so this won’t work to reproduce the content, and it’s also inconvenient.
  • include the content into a variable and traverse the tree to find the nodes we need: the template is wrapped in a big context block so that’s not gonna work. but this would probably break eventually anyway.
and one ugly but working solution

textbook.typ:

#let problems = ()

= First problem
#problems.push(problem($ 2 + 2 = ? $))
#problems.last()

workbook.typ:

= List of textbook problems
#import "textbook.typ"
#textbook.problems.join()

any help is appreciated :D

2 Likes

I actually like your “ugly” solution. It’s simple, reliable and flexible in a way that you won’t get from any hack based on query (this might change if/when Typst implements a solution for Query external typst document · Issue #5218 · typst/typst · GitHub).

One alternative that I sometimes use is to put all the problems in a third file, say problems.typ. You make sure each problem is wrapped in a block or figure or whatever, so when you include it in another file you can easily get an array of values:

// problems.typ
#problem[Problem 1...] // makes a block

#problem[Problem 2...] // makes a block
// textbook.typ or workbook.typ
#let problems = (include "problems.typ").children.filter(x => x.func() == block)

The problems.typ doesn’t use a template so you can introspect it at least at the level of a sequence of blocks, and at the same time you can write all the problems in simple markup without push() etc. The downside of course is that you have to decouple the definition of a problem from its use in the textbook.

1 Like

thanks for the issue link! indeed, that feature is probably necessary if i wanna implement a nicer solution (funny how the comment there is basically my exact use case). i hadn’t thought of the separate file idea, that’s not bad!! will see what works best : )

1 Like