How can I change the heading numbering for headings of different levels differently?

I want my top levels headings be numbered like this “TOP 1: First Name”, “TOP 2: Second name”, but my second level headings just “1.1 first subsection”, “1.2 second subsection”.

If I set my numbering e.g. with

#set heading(numbering: "TOP 1.1")

then the result is
image
which totally makes sense. I think for this I need a show rule, but the naive

#show heading.where(
  level: 1
): it => {
  "TOP" + it
}

returns
image
which ist again not, what I want.

The numbering parameter can also take a custom function. This is what you want to use in this case. I wrote an example below with some comments on what is happening. I hope that it all makes sense!

#set page(width: 5cm, height: auto)

// The function is passed a variable
// number of arguments (e.g. 1, 1, 2).
// The `..nums` is an argument sink,
// which collects all excess arguments
// in `nums`.
#set heading(numbering: (..nums) => {
   // We want the positional arguments
   // from `nums`.
   // `numbers` is now an array of ints
   // e.g. a level one heading `= ...`  could be (1,)
   // e.g. a level two heading `== ...` could be (2, 1)
   // e.g. a level three heading `=== ...` could be (1, 0, 2)
   let numbers = nums.pos()
   // Top level (level 1) headings will have a single
   // int like (1,), as mentioned above. So we check
   // if the array's length is 1 for level one headings.
   if numbers.len() == 1 {
      numbering("TOP 1:", ..numbers)
   } else {
      // Everything else
      numbering("1.1", ..numbers) 
   }
})

= Test
== Test
=== Test
== Test
== Test
= Test
=== Test

test

Use numbly.

#import "@preview/numbly:0.1.0": numbly
#set heading(
  numbering: numbly(
    "Top {1}:",
    "{1}.{2}.",
  ),
)

= First section
== Unterpunkt
== Zweiter Unterpunkt

gives

Top 1: First section
1.1. Unterpunkt
1.2. Zweiter Unterpunkt

Thank you both for answering! :)

I am going to use numbly, because it feels much nicer and cleaner, so thank you for suggesting it, but I chose @mkorje’s answer as the solution, because it explains how it works in more detail and only with that knowledge I understand what exactly numbly does, why it works and how to use it exactly.

For both the numbly and vanilla solution, cross references to the section appear as “Section TOP 1”. How can I make them appear as just “TOP 1”?

#import "@preview/numbly:0.1.0": numbly
#set heading(
  numbering: numbly(
    "Top {1}:",
    "{1}.{2}.",
  ),
)

= First section<sec:top>

@sec:top

== Unterpunkt
== Zweiter Unterpunkt

I don’t know hot to do it automatically completely right, but you could try to tinker with a show rule on the Ref-function.

Just set the supplement parameter of heading to none. See: Heading Function – Typst Documentation

#set heading(supplement: none, ...)

If you want to set it per level, you can use a show set rule. E.g.

#show heading.where(level: 1): set heading(supplement: none)
#show heading.where(level: 2): set heading(supplement: "TOP ")