How can I produce two outputs from one typ file, one with the full document and the other with only a subset of selected figures with caption?

Hey,

I’d like to produce two outputs from one typst file. I’m a teacher, one would be the full document that I’d share online with the students. The other would only contain the figures that the students need to draw on. This output is meant to be printed.

I’d like to be able to produce the two outputs from the same typst file with a command line input argument, like shown in this post : Can I configure my document (e.g. draft/release version, color theme) when creating a PDF without modifying the Typst file directly?
Figures which have the to-print argument set to true would show-up on this output, while everything else would not show up (except maybe the title, headers and footers). The figure number in the caption need to be the same on both outputs.

To achieve this, I need to make a show rule to show nothing but the figures with the to-print argument set to true. Is that doable ?

A non-working minimal example of what I’m trying to achieve would look like this. I tried to load the figures I want to print in a state array, and to call them if compiled with an input cli argument. However the show: none rule called after to negate all other content prevents the state from being updated so the output is empty. Moreover the figure number are not the same as in the full output document. Any help would be appreciated !

File main.typ

#import "mypackage.typ": config, myfig,
#import "mytemplate.typ": mytemplate, 

#let cfg = config(sys.inputs)
#show: mytemplate.with(cfg,)

// This text doesn't show up on the to-print output
Some text

// This figure doesn't go to the to-print output.
#myfig(
    rect(fill: blue, width: 100%, height: 5cm),
    caption: [A blue rectangle],
    to-print: false,
)

// This figure does.
#myfig(
    rect(fill: green, width: 100%, height: 5cm),
    caption: [A green rectangle],
    to-print: true,
)

File mytemplate.typ

#import "mypackage.typ": toprint-array,

#let mytemplate(cfg, body) = {
    if cfg.output-mode == "print" {
        for fig in toprint-array.final() {
            fig
        }
        // This doesn't work as nothing is shown, so the state array with the figures to print doesn't contain anything
        show: none
        body
    } else {
        body
    }
}

File mypackage.typ

#let config(inputs) = {
    let mode = if "output-mode" not in inputs.keys() {
        "full"
    } else {
        inputs.at("output-mode")
    }
    let cfg = (
        output-mode: mode
    )
    cfg
}

#let toprint-array = state("toprint-figs", ())
#let myfig(to-print, ..args) = {
    let fig = figure(..args)
    if to-print {
        toprint-array.update(arr => {arr.push(fig); arr})
    }
    fig
}

My first though went another direction: Any figure that may or may not get printed should be created with a function that either places a figure in the document, or steps the figure counter by one.

//A couple things just to demonstrate
#set figure(caption: [A figure])
#let dummy-image = box(width: 1cm, height: 1cm, stroke: 1pt)

//This would be created as you already are doing, but could also be a simple boolean
#let cfg = (
  output-mode: true
)

//Function that optionally includes the figure, but always steps the figure counter
#let myfig(cfg, ..args) = {
  if cfg.output-mode {
    figure(..args)
  } else {
    context counter(std.figure.where(kind: image)).step()
  }
}

#outline(target: std.figure.where(kind: image))

#figure(dummy-image)

#myfig(cfg, dummy-image)

#figure(dummy-image)

This results in either:


or, with output-mode: false:

You can pair it with sys.inputs, if CLI is used.