Custom the outline as "Figure S1 Second*", update the following code into typst 0.13?

I use the following code to make the outline in bold style and S “*Figure S1 * The first curious figure” in the outline in the previous version of typst.

How to update it to the newest typst 0.13?

// from: https://github.com/typst/typst/discussions/4515
#show outline.entry: it=> if <no-prefix> != it.fields().at(
    default: {},
    "label",
) [ 
    #outline.entry(
    it.level,
    it.element,
    {
        [*#it.element.caption.supplement S*]
        [*#numbering(
            it.element.numbering,
            ..it.element.counter.at(it.element.location()),
        )*]
        [*.* ]
        // figure.caption.separator
        it.element.caption.body
    },
    it.fill,
    [#it.page]
)<no-prefix>] else {it}

The complete code here:

#set page("a4", margin: auto)

// Modify bold "Figure S1 in the outline
// from: https://github.com/typst/typst/discussions/4515
#show outline.entry: it=> if <no-prefix> != it.fields().at(
    default: {},
    "label",
) [ 
    #outline.entry(
    it.level,
    it.element,
    {
        [*#it.element.caption.supplement S*]
        [*#numbering(
            it.element.numbering,
            ..it.element.counter.at(it.element.location()),
        )*]
        [*.* ]
        // figure.caption.separator
        it.element.caption.body
    },
    it.fill,
    [#it.page]
)<no-prefix>] else {it}

// 表格标题粗体
#show figure.caption: it => context[
    *#it.supplement~S#it.counter.display().*~#it.body
]

// Long and short captions for the outline
#let in-outline = state("in-outline", false)
#show outline: it => {
    in-outline.update(true)
    it
    in-outline.update(false)
}
#let ffcaption(long, short) = context if in-outline.get() { short } else { long }

#outline(
title: text(size: 1.2em)[Contents#v(1em)],
target: figure.where(kind: image).or(figure.where(kind: table)),
)

= Red Pic
#figure(
    rect(height:6cm, width: 11cm, fill: red),
    caption: ffcaption([The fisrt curious figure.], [Short1]),
) <fig1>

#figure(
    rect(height:10cm, width: 5cm, fill: red),
    caption: ffcaption([The second curious figure.], [Short2]),
) <fig2>

= Blue Pic
#figure(
    rect(height:2cm, width: 8cm, fill: blue),
    caption: ffcaption([The third curious figure.], [Short3]),
) <fig3>

you can simplify all of your code, up to the show figure.caption, like this:

#set figure(numbering: "S1.")
#show outline.entry: it => link(it.element.location(), it.indented(strong(it.prefix()), it.inner()))
#show figure.caption: it => context box[*#it.supplement #it.counter.display()* #it.body]

(fixed, thanks xkevio!)

You might need to wrap the it.indented in a link() to it.element.location() otherwise the entry is no longer clickable. See Outline Function – Typst Documentation.

2 Likes

What’s your version (mine is 0.13.1), It shows the error:

error: unknown variable: indented
   ┌─ tta1.typ:16:55
   │
16 │ #show outline.entry: it => link(it.element.location(), indented(strong(it.prefix()), it.inner()))
   │                                                        ^^^^^^^^

Also, I would prefer separate links similar to this by query chatgpt:

#show outline.entry: it => text(
    // font: "sans",
    // size: 10pt,
    // spacing: 0.5em,
    link(it.element.location(), strong(it.prefix())) + " " +
    link(it.element.location(), it.inner()) + " \n"
  )

However, It seem not works well with the function of ffcation.
The content in outline is the same as the figure legend, and not use short caption as “Short1”.

typo, it’s supposed to say it.indented(). as for the two separate links, you shouldn’t use a space because that’s not what outlines use. instead you should just link the prefix and the inner separately, like this:

#show outline.entry: it => {
  it.indented(
    link(it.element.location(), strong(it.prefix())),
    link(it.element.location(), it.inner())
  )
}
this should work fine. here's the full source so you can compare it with yours in case it still doesn't work
#set figure(numbering: "S1.")
#show outline.entry: it => {
  it.indented(
    link(it.element.location(), strong(it.prefix())),
    link(it.element.location(), it.inner())
  )
}
#show figure.caption: it => context box[*#it.supplement #it.counter.display()* #it.body]

#let in-outline = state("in-outline", false)
#show outline: it => in-outline.update(true) + it + in-outline.update(false)
#let ffcaption(long, short) = context if in-outline.get() { short } else { long }

#outline(
  title: text(size: 1.2em)[Contents#v(1em)],
  target: figure.where(kind: image).or(figure.where(kind: table)),
)

= Red Pic
#figure(
    rect(height:6cm, width: 11cm, fill: red),
    caption: ffcaption([The fisrt curious figure.], [Short1]),
) <fig1>

#figure(
    rect(height:10cm, width: 5cm, fill: red),
    caption: ffcaption([The second curious figure.], [Short2]),
) <fig2>

= Blue Pic
#figure(
    rect(height:2cm, width: 8cm, fill: blue),
    caption: ffcaption([The third curious figure.], [Short3]),
) <fig3>
1 Like

That works great.

I would also whether we could split the it.inner() into such as it.leader/it.titles/it.page? I’m just curious if we could redefine the outline entirely.

yup! check the docs for outline.entry. the different parts of the entry are accessible through fields and methods. specifically, it.inner() is split into it.element.body, it.fill, it.element.location().page(). i find it useful to hover over each of these to get a tooltip describing what they contain exactly. you can use all of this information to build your own formatting (which is similar to what you did when you used a space + " " +!)

The docs are not clear…

If you know the right style, please let me know. The following code has errors in it.element.body() and it.fill():

#show outline.entry: it => {
  it.indented(
    // red prefix
    link(it.element.location(), strong(text(fill: red, size: 1.2em, it.prefix()))),
    // // inner
    // link(it.element.location(), text(fill: blue, size: 1.1em, it.inner())),
    link(it.element.location(), text(fill: blue, size: 1.1em, it.element.body())),
    link(it.element.location(), text(fill: cyan, size: 1.1em, it.fill())),
    link(it.element.location(), text(fill: green, size: 1.1em, it.page()))
  )
}

body and fill are fields, not methods, so you shouldn’t call them. the error messages say that (but i get it, i forget if something’s a field or a method all the time):

Error: Element entry has no method fill

How to fix this?
→ Did you mean to access the field fill ?

also, my bad: you’re not supposed to use it.element.body (that gives you the whole figure content), but rather it.element.caption.body (naturally, that gives you just the figure caption)