Using flex-caption from a template yields long captions instead of short

Version/platform info: typst version: 0.14.0 (dd1e6e94) (typst-aarch64-apple-darwin)

Various examples online define a flex-caption function to generate long vs short captions for “List of Figures” outlines. I cannot get it to work properly when doing #import.

I have two files: lib_mwe.typ

#let in-outline = state("in-outline", false)

#show outline: it => {
    in-outline.update(true)
    it
    in-outline.update(false)
}

#let flex-caption(short: none, long: none) = context if state("in-outline", false).get() {
    short
} else { long }

#let custom-outline(..args) = {
    show outline: set heading(outlined: true)
    outline(..args)
}

and main.typ

#import("./lib_mwe.typ"): *

#outline()
#pagebreak()

#custom-outline(title: "List of Figures", target: figure.where(kind: image))
#pagebreak()

#figure(
    image("./image.png", width: 60%),
    caption: flex-caption(
        short: [short caption],
        long: [long caption],
    )
)

Compiling main.typ gives me the “long” caption" in the “List of figures” instead of the “short caption”.
Copy-pasting the contents of lib_mwe.typ into main.typ yields the desired behavior. I suspect state-related issues are at play, but they are beyond me at this point.

The reason is actually not about state, but about styles and templates. import just imports the functions and definitions from lib_mwe.typ. The style you applied in the lib file with show has no effect outside its own file.

The full documentation for how to define a style (a template) in one file and use it in another is here: Making a Template – Typst Documentation

You’d need to turn this into a template:

#show outline: it => {
    in-outline.update(true)
    it
    in-outline.update(false)
}

So that you can import a function for that and apply it to your actual document in main.typ.

As an alternative to turning it into a template, the style would need to be applied inside a function like for example the custom-outline function. Then it would have effect there.

1 Like

Thanks for the quick feedback. I’ve tried replacing the bare #show outline directive in lib_mwe.typ with

#let template-outline = it => {
    in-outline.update(true)
    it
    in-outline.update(false)
}

and then in main.typ

#import("./lib_mwe.typ"): *
#show: template-outline

and this seems to have worked. Is this what you meant? Is there a way that would not require calling #show within main.typ?

That’s not quite equivalent, this is the style you want to apply:

#show outline: it => {
  in-outline.update(true)
  it
  in-outline.update(false)
}

And now we write a function that applies this style to a document:

#let template(doc) = {
  show outline: it => {
    in-outline.update(true)
    it
    in-outline.update(false)
  }
  doc
}

As an alternative to turning it into a template, the style would need to be applied inside a function like for example the custom-outline function. Then it would have effect there.

Hey there. Make sure you carefully read How to post in the Questions category before posting questions. The post can be edited.

So this did indeed work, thanks. But I’m unclear what it does differently than my template-outline suggestion. When using your template function, I still needed to do #show template in main.typ. Is this expected? If so, why would typst require (seemingly, from my naive perspective) duplicated show calls (within template and in main.typ) ? At the risk of derailing this question, I guess I also don’t understand how to use the doc argument to template.

In this code, when you call #show: template-outline then it is the document. But originally, it was just the outline. That’s why the translation was not equivalent. It could have been made equivalent by applying it as #show outline: template-outline but I wanted to show you how to make a proper template instead.

Yes

They shouldn’t really be duplicated. The original one you had moves into a function, where it’s not used, only defined. Then you use show in main.typ to invoke that template. Maybe that makes sense?

Interesting, this made me understand a nuance of #show that had so far eluded me.

yes. thanks again.