smarana 0.7.3

An extensible note taking system for typst.
// Appendix — Tag Index Pages
// Auto-generated pages listing all notes for each tag

#import "document.typ": theme-mode
#import "theme.typ": palettes

#let make-tag-slug(tag) = {
  let slug = tag.replace(regex("[^a-zA-Z0-9]+"), "-").trim("-")
  if slug == "" { "untitled" } else { slug }
}

// Renders a single tag index page
// tag: the tag name string
// notes: array of (title, label-slug) pairs
#let tag-index-page(tag, notes) = {
  let colors = palettes.at(theme-mode).appendix

  page(
    fill: colors.outer,
    margin: 0.5cm,
    height: auto,
    {
      set text(
        size: 11pt,
        fill: colors.text,
      )

      show ref: it => {
        let make-styled(c) = text(fill: colors.accent, strong(c))

        if it.supplement != auto and it.supplement != none {
          link(it.target, make-styled(it.supplement))
        } else if it.element != none and it.element.func() == heading {
          link(it.target, make-styled(it.element.body))
        } else {
          it
        }
      }

      block(
        width: 100%,
        fill: colors.card,
        radius: 2pt,
        inset: 24pt,
        stroke: 1pt + colors.stroke,
        {
          // Hidden heading for label target (tag page anchor)
          set heading(numbering: "1.1")
          show heading: it => {
            if it.level >= 10 {
              box(width: 0pt, height: 0pt, clip: true, it)
            } else {
              it.body
            }
          }

          // Anchor label for this tag page
          let tag-slug = make-tag-slug(tag)
          [#heading(level: 10)[#tag] #label("tag-" + tag-slug)]

          // Header
          block(width: 100%, {
            grid(
              columns: (1fr, auto),
              align: (left + horizon, right + horizon),
              text(size: 9pt, fill: colors.meta, "TAG INDEX"),
              box(
                fill: colors.tag-bg,
                radius: 4pt,
                inset: (x: 8pt, y: 3pt),
                text(size: 8pt, weight: "bold", fill: colors.accent, "APPENDIX"),
              )
            )
            v(-8pt)

            text(size: 18pt, weight: "bold", fill: colors.accent, "#" + tag)
          })

          v(6pt)
          line(length: 100%, stroke: 0.5pt + colors.line)
          v(8pt)

          // Note list — masonry-style card grid
          set text(fill: colors.text)
          grid(
            columns: (1fr, 1fr),
            gutter: 8pt,
            ..notes.map(note => {
              let (title, slug) = note
              link(label(slug), box(
                width: 100%,
                fill: colors.tag-bg,
                radius: 6pt,
                inset: (x: 12pt, y: 10pt),
                stroke: 0.5pt + colors.stroke,
                {
                  text(size: 10pt, weight: "medium", fill: colors.accent, title)
                }
              ))
            })
          )
        }
      )
    }
  )
}