smarana 0.7.3

An extensible note taking system for typst.
// Atomic Note Styling (Permanent Notes)
// Thematic styling for well-developed, interconnected knowledge

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

#let atomic-style(title: "", summary: "", date: "", time: "", tags: (), uplink: [], body) = {
  let colors = palettes.at(theme-mode).atomic
  
  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
        }
      }

      show link: it => {
        set text(fill: colors.accent)
        strong(underline(stroke: (dash: "dashed", paint: colors.accent, thickness: 0.5pt), offset: 2pt, it))
      }

      show raw.where(block: false): it => box(
        fill: colors.tag-bg,
        radius: 2pt,
        inset: (x: 3pt, y: 0pt),
        outset: (y: 3pt),
        it
      )

      show raw.where(block: true): it => block(
        fill: colors.outer,
        width: 100%,
        inset: 12pt,
        radius: 4pt,
        stroke: 0.5pt + colors.line,
        it
      )

      block(
        width: 100%,
        fill: colors.card,
        radius: 2pt,
        inset: 24pt,
        stroke: 1pt + colors.stroke,
        {
          // Header
          block(width: 100%, {
            // Top bar: Date on left, Type on right
            grid(
              columns: (1fr, auto),
              align: (left + horizon, right + horizon),
              text(size: 9pt, fill: colors.meta, {
                if date != "" and date != none { date }
                if time != "" and time != none {
                  h(6pt)
                  "|"
                  h(6pt)
                  time
                }
              }),
              box(
                fill: colors.tag-bg,
                radius: 4pt,
                inset: (x: 8pt, y: 3pt),
                text(size: 8pt, weight: "bold", fill: colors.accent, "ATOMIC"),
              )
            )
            v(-8pt)

            text(size: 18pt, weight: "bold", fill: colors.accent, title)

            // Summary
            if summary != "" and summary != none and summary != [] {
              v(4pt)
              text(size: 10pt, style: "italic", fill: colors.summary, summary)
            }

            // Footer bar: Uplink on left, Tags on right
            if (tags != () and tags != none) or (uplink != none and uplink != "" and uplink != () and uplink != []) {
              v(-8pt)
              grid(
                columns: (1fr, auto),
                align: (left + horizon, right + horizon),
                // Uplink (Left)
                if uplink != none and uplink != "" and uplink != () and uplink != [] {
                  text(size: 8pt, fill: colors.accent, {
                    "↑ "
                    uplink
                  })
                } else { [] },
                // Tags (Right)
                if tags != () and tags != none {
                  stack(
                    dir: ltr,
                    spacing: 4pt,
                    ..tags.map(tag => {
                      let tag-slug = tag.replace(regex("[^a-zA-Z0-9]+"), "-").trim("-")
                      let tag-label = "tag-" + if tag-slug == "" { "untitled" } else { tag-slug }
                      link(label(tag-label), box(
                        fill: colors.tag-bg,
                        radius: 4pt,
                        inset: (x: 8pt, y: 3pt),
                        text(size: 8pt, weight: "medium", fill: colors.accent, "#" + tag),
                      ))
                    })
                  )
                } else { [] }
              )
            }
          })

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

          // Body
          set text(fill: colors.text)
          body
        }
      )
    }
  )
}