smarana 0.10.13

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

#import "theme.typ": palettes

#let atomic-style(title: "", summary: "", date: "", time: "", tags: (), uplink: [], theme-mode: "dark", body) = {
  let colors = palettes.at(theme-mode).atomic
  
  page(
    fill: colors.outer,
    margin: 1.2cm,
    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 => {
        if it.lang == "no-box" {
          it
        } else {
          box(
            fill: colors.tag-bg,
            radius: 2pt,
            inset: (x: 3pt, y: 0pt),
            outset: (y: 3pt),
            text(fill: colors.text, it)
          )
        }
      }

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

      show line: it => {
        if it.length == 30pt {
          line(length: 100%, stroke: 1pt + colors.line)
        } else {
          it
        }
      }


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

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

        // Summary
        if summary != "" and summary != none and summary != [] {
          v(-12pt)
          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 != []) {
          grid(
            columns: (1fr, auto),
            align: (left + horizon, right + horizon),
            // Uplink (Left)
            if uplink != none and uplink != "" and uplink != () and uplink != [] {
              text(size: 9pt, 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 = label("tag-" + if tag-slug == "" { "untitled" } else { tag-slug })
                  let pill = box(
                    fill: colors.tag-bg,
                    radius: 4pt,
                    inset: (x: 6pt, y: 2pt),
                    text(size: 10pt, weight: "medium", fill: colors.accent, raw("#" + tag, lang: "no-box")),
                  )
                  context {
                    if query(tag-label).len() > 0 {
                      link(tag-label, pill)
                    } else {
                      pill
                    }
                  }
                })
              )
            } else { [] }
          )
        }
      })

      v(6pt)
      line(length: 100%, stroke: 1pt + colors.line)
      v(-12pt)

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