Is it possible to have multiple outlines?

I try to create a document with multiple tables of contents. For this, I tried to utilize the figure function.

#outline(title: [Lorem & Ipsum], target: figure.where(kind: group1))
#outline(title: [Dolor & Sit],   target: figure.where(kind: group2))
#outline(title: [Misc.],         target: figure.where(kind: group3))

#figure(kind: "group1", supplement: [], [#heading(level: 1)[Lorem]])
#figure(kind: "group1", supplement: [], [#heading(level: 1)[Ipsum]])
#figure(kind: "group2", supplement: [], [#heading(level: 1)[Dolor]])
#figure(kind: "group2", supplement: [], [#heading(level: 1)[Sit]])
#figure(kind: "group3", supplement: [], [#heading(level: 1)[Amet]])

Currently it doesn’t work: there is unknown variable: group1 error. How to fix it?

And also, maybe there is a better way to get multiple tables of contents, without using figure?

1 Like

You need to put group1, etc. in " quotes in the first three lines. To have the figures show up in the outlines, you’ll also need to add captions.

But figures are probably not what you want to use here. What’s the concrete use case? What are these different outlines? For headings you probably just need to find a clever target: ... for the outlines. You can use a selector to target headings that come before or after some point in your document, or headings with different supplements, or headings with particular labels…

1 Like

Here is a real-life example. A book is very thick and deeply structured. There are two outlines: a brief one and a detailed one. The brief is located in the beginning and the detailed in the end.

Brief outline:

Detailed outline:

Like @sijo said, it’d be better to specify the target parameter of outline. But if you are only looking to filter based on the heading level, then you might as well use depth

Summary:

#outline(depth: 2, indent: 1em)

Table of contents:

#outline(indent: 1em)

2 Likes

Hello. Sorry for my delay. I tried to use labels. Could you explain, why the outlines are empty?

#outline(title: [First outline],  target: heading.where(label: "group1"))
#outline(title: [Second outline], target: heading.where(label: "group2"))
#outline(title: [Third outline],  target: heading.where(label: "group3"))

= Lorem <group1>

== A

== B

= Ipsum <group1>

= X

= Y

= Dolor <group2>

= Sit <group2>

= Amet <group3>

You have to give actual labels, not string, so write where(label: <group1>).

By the way if you use these labels only for headings you can simply do target: <group1> :slight_smile:

1 Like

Amazing, thanks! But it turns out there is another issue now. What if I need to create a link to a heading? In such a case, I should use an individual label, like #link(<lorem>)[Link to Lorem], right?

#outline(title: [First outline],  target: heading.where(label: <group1>))
#outline(title: [Second outline], target: heading.where(label: <group2>))
#outline(title: [Third outline],  target: heading.where(label: <group3>))

#pagebreak()
#link(<lorem>)[Link to Lorem]

#lorem(50)

= Lorem
<group1><lorem>

#lorem(500)

= Ipsum
<group1>

#lorem(500)

= Dolor
<group2>

#lorem(500)

= Sit
<group2>

#lorem(500)

= Amet
<group3>

#lorem(500)

And in such a case, though it works, the compiler complains:

warning: content labelled multiple times.
hint: only the last label is used, the rest are ignored

I also tried this:

= Lorem<group1>
~#label("lorem")

And though it works as well, if I click the “Link to Lorem” link, the document is scrolled to somewhat lower than it should be.

I then tried to change it to

= Lorem<lorem>
~#label("group1")

but then the outline is blank again, and I didn’t figured out how to fix it.

Yep you cannot have several labels for the same thing…

One solution would be to remove the <group1> label for this heading and to make the outline with target: selector(<group1>).or(<lorem>). But that’s annoying to keep up to date.

Another solution maybe is to put the extra label just before the heading:

#metadata(none)<lorem>
= Lorem <group1>

I think that way the link might go to the right location, at least if the heading is not placed in a fancy unusual way.

1 Like

And what you think about using #[ ... ]?

#[
= Lorem<group1>
]<lorem>

Instead of labelling every single heading of each group individually, you should probably define a better target in the outline.
Is there a structured rule as to how you label headings by group?

1 Like

I don’t know if this can interfere with other things, like the sticky property of headings… The metadata before the heading seemed less intrusive. But if it works it’s a nice solution.

1 Like

Not sure I understand. If you could provide a simple example of this idea, it would be easier for me to answer :slight_smile:

I was probably confusing. How do you decide which heading belongs in which group, was my question.

[@quachpas] For me, there are two use cases to have multiple outlines.

The first use case is already illustrated by the screenshots above. This is when you need a brief outline and a detailed outline.

The second use case is when you need to combine headings arbitrary. E.g., you have a document with headings A, B, C, and so on, to Z. And your main outline includes all of them. But some sections may have special interest, and you may want to have a second outline specifically for them. In such a case, this can look like:

Main outline (26 elements):

A .....
B .....
C .....
D .....
E .....
.
.
.
Z .....

Additional outline (3 elements):

E .....
M .....
X .....

Hmmm I understand the use case well! My question was more about the labels you use for headings.
When you write

#outline(title: [First outline],  target: heading.where(label: <group1>))
#outline(title: [Second outline], target: heading.where(label: <group2>))
#outline(title: [Third outline],  target: heading.where(label: <group3>))

what exactly is “group1”, “group2”, and “group3”? We will be able to help you better if you tell us that I think

To put the request for more information in another way: what is your rationale for labelling something <group1>? Do all these headings have something in common that we could use in target?

If you mean something in common in their appearance, as if they are red or italic or have specific prefix, the answer is no. The appearance of group1-headings, group2-headings, and group3-headings is the same. The headings are separated into three groups soleley on which things they talk about.

Recently I asked a different question: How to create a link to a section in such a way that link text will be the same as the heading itself, and if the heading will be changed, link text will be updated automatically, i.e. instead of writing #link<lorem>)[Lorem], we could write just #link<lorem>)[] (https://forum.typst.app/t/1221). It was answered by @xkevio.

It seems that though #metadata and #[ ... ] solutions can work with xkevio’s solution in theory, it would be quite fragile. So currently I decided to use the target: selector(<...>).or(<...>) solution, mentioned by @sijo above.

#show link: underline

// https://forum.typst.app/t/1221
#show link.where(body: []): l => {
  let heading-elem = query(l.dest).first()
  link(l.dest, heading-elem.body.text)
}

// --

// https://forum.typst.app/t/1307
#outline(title: [First outline],  target: selector(<group1>).or(<lorem>).or(<ipsum>))
#outline(title: [Second outline], target: selector(<group2>).or(<dolor>))
#outline(title: [Third outline],  target: selector(<group3>))

#pagebreak()
#link(<lorem>)[] \
#link(<ipsum>)[] \
#link(<dolor>)[]

#lorem(50)

= Lorem
<lorem>

#lorem(325)

= Ipsum
<ipsum>

#lorem(325)

= Dolor
<dolor>

#lorem(325)

= Sit
<group2>

#lorem(325)

= Amet
<group3>

#lorem(325)

Glad you got a solution!

If the headings are labelled based on their contents, I think you might as well have a super-heading grouping them. If you don’t like having a “chapter” heading, then you can reserve level 1 for invisible headings, and just use it for selection purposes, i.e. all select all headings after chapter, before next chapter.

1 Like

You can reserve level 1 for invisible headings, and just use it for selection purposes, i.e. all select all headings after chapter, before next chapter.

This seems to be an interesting idea, but could you show how to do it? It seems it is not something built-in into the #outline function, and we need hand-script it, right?