ideate
This commit is contained in:
@@ -17,6 +17,18 @@ Two modes coexist. Use whichever fits the moment.
|
|||||||
### Mixing Freely
|
### Mixing Freely
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
|
(def title "My Document")
|
||||||
|
(def data [{:name "Alice" :value 100}
|
||||||
|
{:name "Bob" :value 200}])
|
||||||
|
|
||||||
|
(defn calculate-total [rows]
|
||||||
|
(reduce + (map :value rows)))
|
||||||
|
|
||||||
|
(defn render-table [rows]
|
||||||
|
[:table {:columns 2}
|
||||||
|
(for [row rows]
|
||||||
|
(list (:name row) (:value row)))])
|
||||||
|
|
||||||
[;; Hiccup for structured parts
|
[;; Hiccup for structured parts
|
||||||
[:heading {:level 1} title]
|
[:heading {:level 1} title]
|
||||||
|
|
||||||
@@ -30,12 +42,10 @@ Two modes coexist. Use whichever fits the moment.
|
|||||||
"
|
"
|
||||||
|
|
||||||
;; Back to hiccup for programmatic stuff
|
;; Back to hiccup for programmatic stuff
|
||||||
[:table {:columns 2}
|
(render-table data)
|
||||||
(for [row data]
|
|
||||||
(list (:name row) (:value row)))]
|
|
||||||
|
|
||||||
;; Raw with interpolation
|
;; Raw with interpolation
|
||||||
#t"The answer is ~(calculate-answer data)."
|
#t"The total is ~(calculate-total data)."
|
||||||
|
|
||||||
;; Nested: raw inside hiccup
|
;; Nested: raw inside hiccup
|
||||||
[:block {:inset "1em" :fill "gray.lighten(90%)"}
|
[:block {:inset "1em" :fill "gray.lighten(90%)"}
|
||||||
@@ -46,12 +56,35 @@ Two modes coexist. Use whichever fits the moment.
|
|||||||
= Section
|
= Section
|
||||||
|
|
||||||
Here's a dynamic table:
|
Here's a dynamic table:
|
||||||
~(render-table results)
|
~(render-table data)
|
||||||
|
|
||||||
And back to prose.
|
And back to prose.
|
||||||
"]
|
"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
#heading(level: 1)[My Document]
|
||||||
|
|
||||||
|
This is just regular Typst content. I can write *bold* and _italic_
|
||||||
|
without all the brackets. Much nicer for prose.
|
||||||
|
|
||||||
|
- Bullet one
|
||||||
|
- Bullet two
|
||||||
|
|
||||||
|
#table(columns: 2, [Alice], [100], [Bob], [200])
|
||||||
|
|
||||||
|
The total is 300.
|
||||||
|
|
||||||
|
#block(inset: 1em, fill: gray.lighten(90%))[This is a _sidebar_ with easy formatting.]
|
||||||
|
|
||||||
|
= Section
|
||||||
|
|
||||||
|
Here's a dynamic table:
|
||||||
|
#table(columns: 2, [Alice], [100], [Bob], [200])
|
||||||
|
|
||||||
|
And back to prose.
|
||||||
|
```
|
||||||
|
|
||||||
### Raw Escape Syntax
|
### Raw Escape Syntax
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
@@ -74,15 +107,38 @@ Using `~()` - the unquote concept from Clojure's syntax-quote:
|
|||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
;; Simple expression
|
;; Simple expression
|
||||||
#t"The value is ~(expr)."
|
(def answer 42)
|
||||||
|
#t"The value is ~answer."
|
||||||
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
The value is 42.
|
||||||
|
```
|
||||||
|
|
||||||
|
```clojure
|
||||||
;; Hiccup expression returns content
|
;; Hiccup expression returns content
|
||||||
|
(def url "https://example.com")
|
||||||
|
(def label "here")
|
||||||
#t"Click ~([:link {:src url} label]) to continue."
|
#t"Click ~([:link {:src url} label]) to continue."
|
||||||
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
Click #link("https://example.com")[here] to continue.
|
||||||
|
```
|
||||||
|
|
||||||
|
```clojure
|
||||||
;; Conditional
|
;; Conditional
|
||||||
#t"Status: ~(if done? 'Complete' 'Pending')"
|
(def done true)
|
||||||
|
#t"Status: ~(if done 'Complete' 'Pending')"
|
||||||
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
Status: Complete
|
||||||
|
```
|
||||||
|
|
||||||
|
```clojure
|
||||||
;; Loop
|
;; Loop
|
||||||
|
(def items [{:name "Apples"} {:name "Oranges"}])
|
||||||
#t"
|
#t"
|
||||||
= Items
|
= Items
|
||||||
~(for [i items]
|
~(for [i items]
|
||||||
@@ -90,6 +146,12 @@ Using `~()` - the unquote concept from Clojure's syntax-quote:
|
|||||||
"
|
"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
= Items
|
||||||
|
- Apples
|
||||||
|
- Oranges
|
||||||
|
```
|
||||||
|
|
||||||
## Hiccup Syntax
|
## Hiccup Syntax
|
||||||
|
|
||||||
All elements follow standard Hiccup:
|
All elements follow standard Hiccup:
|
||||||
@@ -352,54 +414,112 @@ It's a Lisp - of course we have macros.
|
|||||||
(list ~@body)))
|
(list ~@body)))
|
||||||
|
|
||||||
;; Use it
|
;; Use it
|
||||||
(when-content show-abstract?
|
(def show-abstract true)
|
||||||
|
(def abstract-text "This paper presents...")
|
||||||
|
|
||||||
|
(when-content show-abstract
|
||||||
[:heading {:level 2} "Abstract"]
|
[:heading {:level 2} "Abstract"]
|
||||||
abstract-text)
|
abstract-text)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
== Abstract
|
||||||
|
This paper presents...
|
||||||
|
```
|
||||||
|
|
||||||
### DSL Macros
|
### DSL Macros
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
;; Math DSL
|
;; Shorthand macros for references
|
||||||
(defmacro math-block [& exprs]
|
|
||||||
`[:math {:block true}
|
|
||||||
~(compile-math-dsl exprs)])
|
|
||||||
|
|
||||||
(math-block
|
|
||||||
(sum :i 0 :n
|
|
||||||
(frac (pow x i) (factorial i))))
|
|
||||||
;; => $ sum_(i=0)^n x^i / i! $
|
|
||||||
|
|
||||||
;; Shorthand for references
|
|
||||||
(defmacro ref [id]
|
(defmacro ref [id]
|
||||||
`[:ref {:target ~id}])
|
`[:ref {:target ~id}])
|
||||||
|
|
||||||
(defmacro cite [& keys]
|
(defmacro cite [& keys]
|
||||||
`[:cite {:keys [~@keys]}])
|
`[:cite {:keys [~@keys]}])
|
||||||
|
|
||||||
(ref :fig/arch) ; => [:ref {:target :fig/arch}]
|
(ref :fig/arch)
|
||||||
(cite :smith2020 :jones2021) ; => [:cite {:keys [:smith2020 :jones2021]}]
|
(cite :smith2020 :jones2021)
|
||||||
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
@fig:arch
|
||||||
|
@smith2020 @jones2021
|
||||||
|
```
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
;; Math helper functions
|
||||||
|
(defn sum [var from to body]
|
||||||
|
(str "sum_(" var "=" from ")^" to " " body))
|
||||||
|
|
||||||
|
(defn frac [num denom]
|
||||||
|
(str "(" num ") / (" denom ")"))
|
||||||
|
|
||||||
|
(defn pow [base exp]
|
||||||
|
(str base "^" exp))
|
||||||
|
|
||||||
|
;; Math block macro
|
||||||
|
(defmacro math-block [& body]
|
||||||
|
`[:math {:block true} (str ~@body)])
|
||||||
|
|
||||||
|
(math-block (sum "i" 0 "n" (frac (pow "x" "i") "i!")))
|
||||||
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
$ sum_(i=0)^n (x^i) / (i!) $
|
||||||
```
|
```
|
||||||
|
|
||||||
### Template Macros
|
### Template Macros
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
|
;; Helper functions for template rendering
|
||||||
|
(defn render-author [{:keys [name affil]}]
|
||||||
|
[:block {:align "center"}
|
||||||
|
[:strong name] [:linebreak]
|
||||||
|
[:emph affil]])
|
||||||
|
|
||||||
|
(defn render-authors [authors]
|
||||||
|
(map render-author authors))
|
||||||
|
|
||||||
|
(defn render-abstract [text]
|
||||||
|
[[:heading {:level 2} "Abstract"]
|
||||||
|
[:block {:inset "1em"} [:emph text]]])
|
||||||
|
|
||||||
|
;; Template macro - captures body and prepends document setup
|
||||||
|
(defmacro deftemplate [name params & preamble]
|
||||||
|
`(defmacro ~name [~@params & ~'body]
|
||||||
|
(concat ~@preamble ~'body)))
|
||||||
|
|
||||||
(deftemplate ieee-paper [title authors abstract]
|
(deftemplate ieee-paper [title authors abstract]
|
||||||
[[:set {:element :page :paper "us-letter" :columns 2}]
|
[[:set {:element :page :paper "us-letter" :columns 2}]
|
||||||
[:heading {:level 1 :align "center"} ~title]
|
[:set {:element :text :size "10pt"}]
|
||||||
(render-authors ~authors)
|
[:heading {:level 1 :align "center"} title]
|
||||||
(render-abstract ~abstract)
|
(render-authors authors)
|
||||||
~'&body])
|
(render-abstract abstract)])
|
||||||
|
|
||||||
|
;; Use the template
|
||||||
(ieee-paper
|
(ieee-paper
|
||||||
"Neural Networks for Fun and Profit"
|
"Neural Networks for Fun and Profit"
|
||||||
[{:name "Alice" :affil "MIT"}]
|
[{:name "Alice" :affil "MIT"}]
|
||||||
"We present..."
|
"We present a novel approach..."
|
||||||
|
|
||||||
[:heading {:level 2} "Introduction"]
|
[:heading {:level 2} "Introduction"]
|
||||||
"..."
|
"The problem of machine learning..."
|
||||||
|
|
||||||
[:heading {:level 2} "Methods"]
|
[:heading {:level 2} "Methods"]
|
||||||
"...")
|
"Our method consists of...")
|
||||||
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
#set page(paper: "us-letter", columns: 2)
|
||||||
|
#set text(size: 10pt)
|
||||||
|
#heading(level: 1, align: center)[Neural Networks for Fun and Profit]
|
||||||
|
#block(align: center)[#strong[Alice] \ #emph[MIT]]
|
||||||
|
== Abstract
|
||||||
|
#block(inset: 1em)[#emph[We present a novel approach...]]
|
||||||
|
== Introduction
|
||||||
|
The problem of machine learning...
|
||||||
|
== Methods
|
||||||
|
Our method consists of...
|
||||||
```
|
```
|
||||||
|
|
||||||
## Full Example
|
## Full Example
|
||||||
@@ -482,6 +602,66 @@ It's a Lisp - of course we have macros.
|
|||||||
(compile-to-typst paper "paper.typ")
|
(compile-to-typst paper "paper.typ")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```typst
|
||||||
|
#set page(paper: "a4")
|
||||||
|
#set text(font: "Linux Libertine")
|
||||||
|
|
||||||
|
#heading(level: 1)[A Very Important Paper]
|
||||||
|
|
||||||
|
#block(align: center)[
|
||||||
|
#block(inset: 1em)[
|
||||||
|
#strong[Alice Chen] \
|
||||||
|
#emph[MIT] \
|
||||||
|
#link("mailto:alice@mit.edu")[alice@mit.edu]
|
||||||
|
]
|
||||||
|
#h(2em)
|
||||||
|
#block(inset: 1em)[
|
||||||
|
#strong[Bob Smith] \
|
||||||
|
#emph[Stanford] \
|
||||||
|
#link("mailto:bob@stanford.edu")[bob@stanford.edu]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
= Abstract
|
||||||
|
|
||||||
|
We present a novel approach to solving important problems.
|
||||||
|
Our method achieves *state-of-the-art* results on several
|
||||||
|
benchmarks while maintaining computational efficiency.
|
||||||
|
|
||||||
|
= Introduction
|
||||||
|
|
||||||
|
The problem of _important things_ has long challenged researchers.
|
||||||
|
Previous approaches @citation1 @citation2 have made progress but
|
||||||
|
fundamental limitations remain.
|
||||||
|
|
||||||
|
#figure(
|
||||||
|
image("architecture.png", width: 80%),
|
||||||
|
caption: [System architecture overview.]
|
||||||
|
) <fig:arch>
|
||||||
|
|
||||||
|
Our method works by first processing the input through
|
||||||
|
the encoder (see @fig:arch).
|
||||||
|
|
||||||
|
= Results
|
||||||
|
|
||||||
|
#table(
|
||||||
|
columns: 2,
|
||||||
|
align: (left, right),
|
||||||
|
[#strong[Method]], [#strong[Accuracy]],
|
||||||
|
[Baseline], [72.0%],
|
||||||
|
[Ours], [94.0%]
|
||||||
|
)
|
||||||
|
|
||||||
|
As shown in the table above, our method significantly
|
||||||
|
outperforms the baseline.
|
||||||
|
|
||||||
|
= Conclusion
|
||||||
|
|
||||||
|
We have demonstrated that our approach is highly effective.
|
||||||
|
|
||||||
|
#bibliography("refs.bib")
|
||||||
|
```
|
||||||
|
|
||||||
## Rules Summary
|
## Rules Summary
|
||||||
|
|
||||||
1. **`#t"..."`** - raw Typst content, returned as-is
|
1. **`#t"..."`** - raw Typst content, returned as-is
|
||||||
|
|||||||
-381
@@ -1,381 +0,0 @@
|
|||||||
# Dual Mode: Hiccup + Raw Escapes
|
|
||||||
|
|
||||||
Both systems coexist. Use whichever fits the moment.
|
|
||||||
|
|
||||||
## The Two Modes
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; HICCUP - when you need programmatic structure
|
|
||||||
[:heading {:level 1} "Title"]
|
|
||||||
|
|
||||||
;; RAW - when you just want to write
|
|
||||||
#t"= Title"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mixing Freely
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
[;; Hiccup for structured parts
|
|
||||||
[:heading {:level 1} title]
|
|
||||||
|
|
||||||
;; Raw escape for prose-heavy sections
|
|
||||||
#t"
|
|
||||||
This is just regular Typst content. I can write *bold* and _italic_
|
|
||||||
without all the brackets. Much nicer for prose.
|
|
||||||
|
|
||||||
- Bullet one
|
|
||||||
- Bullet two
|
|
||||||
"
|
|
||||||
|
|
||||||
;; Back to hiccup for programmatic stuff
|
|
||||||
[:table {:columns 2}
|
|
||||||
(for [row data]
|
|
||||||
(list (:name row) (:value row)))]
|
|
||||||
|
|
||||||
;; Raw with interpolation
|
|
||||||
#t"The answer is ~(calculate-answer data)."
|
|
||||||
|
|
||||||
;; Nested: raw inside hiccup (use :block for container)
|
|
||||||
[:block {:inset "1em" :fill "gray.lighten(90%)"}
|
|
||||||
#t"This is a _sidebar_ with easy formatting."]
|
|
||||||
|
|
||||||
;; Nested: hiccup inside raw
|
|
||||||
#t"
|
|
||||||
= Section
|
|
||||||
|
|
||||||
Here's a dynamic table:
|
|
||||||
~(render-table results)
|
|
||||||
|
|
||||||
And back to prose.
|
|
||||||
"]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Escape Syntax
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Short string
|
|
||||||
#t"= Heading"
|
|
||||||
|
|
||||||
;; Multi-line block
|
|
||||||
#t"
|
|
||||||
Multiple lines
|
|
||||||
of typst content
|
|
||||||
"
|
|
||||||
|
|
||||||
;; Content with quotes (must escape)
|
|
||||||
#t"He said \"hello\" and *left*."
|
|
||||||
```
|
|
||||||
|
|
||||||
## Interpolation Inside Raw
|
|
||||||
|
|
||||||
Using `~()` - the unquote concept from Clojure's syntax-quote:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Simple expression
|
|
||||||
#t"The value is ~(expr)."
|
|
||||||
|
|
||||||
;; Hiccup expression returns content
|
|
||||||
#t"Click ~([:link {:src url} label]) to continue."
|
|
||||||
|
|
||||||
;; Conditional
|
|
||||||
#t"Status: ~(if done? 'Complete' 'Pending')"
|
|
||||||
|
|
||||||
;; Loop
|
|
||||||
#t"
|
|
||||||
= Items
|
|
||||||
~(for [i items]
|
|
||||||
(str '- ' (:name i) '\n'))
|
|
||||||
"
|
|
||||||
|
|
||||||
;; Or loop returning hiccup (items are direct list children)
|
|
||||||
#t"
|
|
||||||
= Items
|
|
||||||
~[:list (for [i items] (:name i))]
|
|
||||||
"
|
|
||||||
```
|
|
||||||
|
|
||||||
Same mental model as macro unquote:
|
|
||||||
```clojure
|
|
||||||
`(let [x ~expr] ...) ; macro: escape to eval
|
|
||||||
#t"Value: ~(expr)" ; template: escape to eval
|
|
||||||
```
|
|
||||||
|
|
||||||
## Full Example
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(ns my-paper.core
|
|
||||||
(:require [clojure-typst.core :refer :all]))
|
|
||||||
|
|
||||||
(def authors
|
|
||||||
[{:name "Alice Chen" :affiliation "MIT" :email "alice@mit.edu"}
|
|
||||||
{:name "Bob Smith" :affiliation "Stanford" :email "bob@stanford.edu"}])
|
|
||||||
|
|
||||||
(def results
|
|
||||||
[{:method "Baseline" :accuracy 0.72}
|
|
||||||
{:method "Ours" :accuracy 0.94}])
|
|
||||||
|
|
||||||
(defn author-card [{:keys [name affiliation email]}]
|
|
||||||
[:block {:inset "1em"}
|
|
||||||
[:strong name] [:linebreak]
|
|
||||||
[:emph affiliation] [:linebreak]
|
|
||||||
[:link {:src (str "mailto:" email)} email]])
|
|
||||||
|
|
||||||
(defn results-table [data]
|
|
||||||
[:table {:columns 2 :align ["left" "right"]}
|
|
||||||
[:strong "Method"] [:strong "Accuracy"]
|
|
||||||
(for [{:keys [method accuracy]} data]
|
|
||||||
(list
|
|
||||||
method
|
|
||||||
(format "%.1f%%" (* 100 accuracy))))])
|
|
||||||
|
|
||||||
(def paper
|
|
||||||
[[:heading {:level 1} "A Very Important Paper"]
|
|
||||||
|
|
||||||
;; Programmatic author list
|
|
||||||
[:block {:align "center"}
|
|
||||||
(interpose [:h "2em"]
|
|
||||||
(map author-card authors))]
|
|
||||||
|
|
||||||
;; Prose in raw mode - much nicer to write
|
|
||||||
#t"
|
|
||||||
= Abstract
|
|
||||||
|
|
||||||
We present a novel approach to solving important problems.
|
|
||||||
Our method achieves *state-of-the-art* results on several
|
|
||||||
benchmarks while maintaining computational efficiency.
|
|
||||||
|
|
||||||
= Introduction
|
|
||||||
|
|
||||||
The problem of _important things_ has long challenged researchers.
|
|
||||||
Previous approaches @citation1 @citation2 have made progress but
|
|
||||||
fundamental limitations remain.
|
|
||||||
|
|
||||||
In this work, we propose a new framework that addresses these
|
|
||||||
limitations directly. Our key contributions are:
|
|
||||||
|
|
||||||
+ A novel architecture for processing data
|
|
||||||
+ Theoretical analysis of convergence properties
|
|
||||||
+ Extensive empirical evaluation
|
|
||||||
|
|
||||||
= Methods
|
|
||||||
"
|
|
||||||
|
|
||||||
;; Back to hiccup for the technical diagram
|
|
||||||
[:figure {:caption "System architecture overview." :label :fig/arch}
|
|
||||||
[:image {:src "architecture.png" :width "80%"}]]
|
|
||||||
|
|
||||||
#t"
|
|
||||||
Our method works by first processing the input through
|
|
||||||
the encoder (see @fig:arch). The encoded representation
|
|
||||||
is then passed to the decoder which produces the output.
|
|
||||||
|
|
||||||
= Results
|
|
||||||
"
|
|
||||||
|
|
||||||
;; Programmatic table
|
|
||||||
(results-table results)
|
|
||||||
|
|
||||||
#t"
|
|
||||||
As shown in the table above, our method significantly
|
|
||||||
outperforms the baseline.
|
|
||||||
|
|
||||||
= Conclusion
|
|
||||||
|
|
||||||
We have demonstrated that our approach is ~(if (> (:accuracy (last results)) 0.9)
|
|
||||||
"highly effective"
|
|
||||||
"somewhat effective").
|
|
||||||
Future work will explore extensions to other domains.
|
|
||||||
"
|
|
||||||
|
|
||||||
;; Bibliography could be generated
|
|
||||||
[:bibliography {:src "refs.bib"}]])
|
|
||||||
|
|
||||||
;; Compile it
|
|
||||||
(compile-to-typst paper "paper.typ")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Macros
|
|
||||||
|
|
||||||
It's a Lisp - of course we have macros.
|
|
||||||
|
|
||||||
### Standard Code Macros
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Threading for cleaner data transforms
|
|
||||||
(defmacro -> [x & forms]
|
|
||||||
...)
|
|
||||||
|
|
||||||
(-> data
|
|
||||||
(filter :active)
|
|
||||||
(map :name)
|
|
||||||
(sort))
|
|
||||||
|
|
||||||
;; Custom control flow
|
|
||||||
(defmacro when-let [[binding expr] & body]
|
|
||||||
`(let [~binding ~expr]
|
|
||||||
(when ~binding ~@body)))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Content Macros
|
|
||||||
|
|
||||||
Macros that expand to hiccup/content:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Define a reusable content pattern
|
|
||||||
(defmacro defcomponent [name args & body]
|
|
||||||
`(defn ~name ~args
|
|
||||||
(list ~@body))) ; sequences flatten into parent content
|
|
||||||
|
|
||||||
;; Conditional content sections
|
|
||||||
(defmacro when-content [test & body]
|
|
||||||
`(when ~test
|
|
||||||
(list ~@body)))
|
|
||||||
|
|
||||||
;; Use it
|
|
||||||
(when-content show-abstract?
|
|
||||||
[:heading {:level 2} "Abstract"]
|
|
||||||
abstract-text)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Document Structure Macros
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Academic paper structure
|
|
||||||
(defmacro paper [& {:keys [title authors abstract] :as opts} & sections]
|
|
||||||
`[[:heading {:level 1} ~title]
|
|
||||||
(author-block ~authors)
|
|
||||||
(when ~abstract
|
|
||||||
(list
|
|
||||||
[:heading {:level 2} "Abstract"]
|
|
||||||
~abstract))
|
|
||||||
~@sections])
|
|
||||||
|
|
||||||
;; Usage
|
|
||||||
(paper
|
|
||||||
:title "My Great Paper"
|
|
||||||
:authors ["Alice" "Bob"]
|
|
||||||
:abstract "We did cool stuff."
|
|
||||||
|
|
||||||
[:heading {:level 2} "Introduction"]
|
|
||||||
"..."
|
|
||||||
|
|
||||||
[:heading {:level 2} "Methods"]
|
|
||||||
"...")
|
|
||||||
```
|
|
||||||
|
|
||||||
### DSL Macros
|
|
||||||
|
|
||||||
Build mini-languages for specific domains:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Math DSL that's easier to write than raw Typst math
|
|
||||||
(defmacro math-block [& exprs]
|
|
||||||
`[:math {:block true}
|
|
||||||
~(compile-math-dsl exprs)])
|
|
||||||
|
|
||||||
(math-block
|
|
||||||
(sum :i 0 :n
|
|
||||||
(frac (pow x i) (factorial i))))
|
|
||||||
;; => $ sum_(i=0)^n x^i / i! $
|
|
||||||
|
|
||||||
;; Table DSL
|
|
||||||
(defmacro deftable [name headers & row-specs]
|
|
||||||
...)
|
|
||||||
|
|
||||||
(deftable results-table
|
|
||||||
["Method" "Precision" "Recall" "F1"]
|
|
||||||
:from results
|
|
||||||
:row (fn [{:keys [method p r f1]}]
|
|
||||||
[method (fmt p) (fmt r) (fmt f1)]))
|
|
||||||
|
|
||||||
;; Figure DSL with auto-numbering
|
|
||||||
(defmacro figure [id & {:keys [src caption width]}]
|
|
||||||
`(do
|
|
||||||
(register-figure! ~id)
|
|
||||||
[:figure {:label ~id :caption ["Figure " (figure-num ~id) ": " ~caption]}
|
|
||||||
[:image {:src ~src :width ~(or width "100%")}]]))
|
|
||||||
|
|
||||||
(figure :arch
|
|
||||||
:src "architecture.png"
|
|
||||||
:caption "System overview"
|
|
||||||
:width "80%")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Syntax Extension Macros
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Custom reader syntax for common patterns
|
|
||||||
;; (would require reader macro support)
|
|
||||||
|
|
||||||
;; Shorthand for references
|
|
||||||
(defmacro ref [id]
|
|
||||||
`[:ref {:target ~id}])
|
|
||||||
|
|
||||||
;; Shorthand for citations
|
|
||||||
(defmacro cite [& keys]
|
|
||||||
`[:cite {:keys [~@keys]}])
|
|
||||||
|
|
||||||
;; Usage
|
|
||||||
(ref :fig/arch) ; => [:ref {:target :fig/arch}]
|
|
||||||
(cite :smith2020 :jones2021) ; => [:cite {:keys [:smith2020 :jones2021]}]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Template Macros
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Define document templates
|
|
||||||
(defmacro deftemplate [name args & structure]
|
|
||||||
`(defmacro ~name ~args
|
|
||||||
~@structure))
|
|
||||||
|
|
||||||
(deftemplate ieee-paper [title authors abstract]
|
|
||||||
[[:set {:element :page :paper "us-letter" :columns 2}]
|
|
||||||
[:heading {:level 1 :align "center"} ~title]
|
|
||||||
(render-authors ~authors)
|
|
||||||
(render-abstract ~abstract)
|
|
||||||
~'&body]) ; splice remaining content
|
|
||||||
|
|
||||||
;; Use template
|
|
||||||
(ieee-paper
|
|
||||||
"Neural Networks for Fun and Profit"
|
|
||||||
[{:name "Alice" :affil "MIT"}]
|
|
||||||
"We present..."
|
|
||||||
|
|
||||||
[:heading {:level 2} "Introduction"]
|
|
||||||
"..."
|
|
||||||
[:heading {:level 2} "Methods"]
|
|
||||||
"...")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Why Macros Matter Here
|
|
||||||
|
|
||||||
| Pattern | Without Macros | With Macros |
|
|
||||||
|---------|---------------|-------------|
|
|
||||||
| Repeated structure | Copy-paste hiccup | `defcomponent` |
|
|
||||||
| Conditional sections | Verbose `if` expressions | `when-content` |
|
|
||||||
| Domain languages | Manual compilation | DSL macros |
|
|
||||||
| Document templates | Function composition | Template macros |
|
|
||||||
| Boilerplate elimination | Nowhere to hide it | Macro it away |
|
|
||||||
|
|
||||||
**The power:** Your document language grows to fit your domain.
|
|
||||||
|
|
||||||
## Rules
|
|
||||||
|
|
||||||
1. **`#t"..."`** - raw Typst content, returned as-is
|
|
||||||
2. **`~(...)`** inside raw - interpolate Clojure expression (like unquote)
|
|
||||||
3. **`[:tag ...]`** - hiccup, compiled to Typst function calls
|
|
||||||
4. **Strings in hiccup** - become Typst content directly
|
|
||||||
5. **Seqs in hiccup** - flattened (so `map`/`for` just work)
|
|
||||||
6. **Sequences flatten** - `(for ...)`, `(map ...)`, `(list ...)` results merge into parent
|
|
||||||
|
|
||||||
## Why Both?
|
|
||||||
|
|
||||||
| Situation | Use |
|
|
||||||
|-----------|-----|
|
|
||||||
| Prose, paragraphs, natural writing | Raw `#t"..."` |
|
|
||||||
| Tables, figures, structured layout | Hiccup |
|
|
||||||
| Loops, conditionals, data-driven | Hiccup or interpolated raw |
|
|
||||||
| Components, reusable parts | Hiccup functions |
|
|
||||||
| Quick one-off formatting | Either, your preference |
|
|
||||||
|
|
||||||
**The principle:** Structure when you need it, prose when you don't.
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
# Hiccup for Typst Content - Prototype
|
|
||||||
|
|
||||||
## The Idea
|
|
||||||
|
|
||||||
Hiccup represents markup as nested data structures:
|
|
||||||
```clojure
|
|
||||||
[:tag {attrs} & children]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Syntax Mapping
|
|
||||||
|
|
||||||
### Clojure-Typst Hiccup → Typst Output
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Heading
|
|
||||||
[:heading {:level 2} "My Section"]
|
|
||||||
;; => #heading(level: 2)[My Section]
|
|
||||||
|
|
||||||
;; Inline formatting (paragraphs are implicit in Typst)
|
|
||||||
["Hello " [:strong "world"] " and " [:emph "goodbye"]]
|
|
||||||
;; => Hello #strong[world] and #emph[goodbye]
|
|
||||||
|
|
||||||
;; Lists (children are direct content, not :item tags)
|
|
||||||
[:list
|
|
||||||
"First thing"
|
|
||||||
"Second thing"
|
|
||||||
[[:strong "Important"] " third thing"]]
|
|
||||||
;; => #list(
|
|
||||||
;; [First thing],
|
|
||||||
;; [Second thing],
|
|
||||||
;; [#strong[Important] third thing]
|
|
||||||
;; )
|
|
||||||
|
|
||||||
;; Tables (cells are direct content, not :cell tags)
|
|
||||||
[:table {:columns 3}
|
|
||||||
"A" "B" "C"
|
|
||||||
"1" "2" "3"]
|
|
||||||
;; => #table(columns: 3,
|
|
||||||
;; [A], [B], [C],
|
|
||||||
;; [1], [2], [3]
|
|
||||||
;; )
|
|
||||||
|
|
||||||
;; Code blocks (use :raw for displayed code)
|
|
||||||
[:raw {:lang "python" :block true} "print('hello')"]
|
|
||||||
;; => #raw(lang: "python", block: true, "print('hello')")
|
|
||||||
|
|
||||||
;; Math (inline by default)
|
|
||||||
[:math "x^2 + y^2 = z^2"]
|
|
||||||
;; => $x^2 + y^2 = z^2$
|
|
||||||
|
|
||||||
;; Math (block/display)
|
|
||||||
[:math {:block true} "sum_(i=0)^n i = (n(n+1))/2"]
|
|
||||||
;; => $ sum_(i=0)^n i = (n(n+1))/2 $
|
|
||||||
|
|
||||||
;; Images (src in attrs, no children)
|
|
||||||
[:image {:src "diagram.png" :width "50%"}]
|
|
||||||
;; => #image("diagram.png", width: 50%)
|
|
||||||
|
|
||||||
;; Links (src in attrs, child is display text)
|
|
||||||
[:link {:src "https://typst.app"} "Typst"]
|
|
||||||
;; => #link("https://typst.app")[Typst]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Full Document Example
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(def my-doc
|
|
||||||
[[:heading {:level 1} "My Paper"]
|
|
||||||
|
|
||||||
"This is the introduction. We discuss "
|
|
||||||
[:emph "important things"] "."
|
|
||||||
|
|
||||||
[:parbreak]
|
|
||||||
|
|
||||||
[:heading {:level 2} "Methods"]
|
|
||||||
|
|
||||||
"We used the following approach:"
|
|
||||||
|
|
||||||
[:list
|
|
||||||
"Step one"
|
|
||||||
"Step two"
|
|
||||||
"Step three"]
|
|
||||||
|
|
||||||
[:heading {:level 2} "Results"]
|
|
||||||
|
|
||||||
"The result is " [:math "x = 42"] "."
|
|
||||||
|
|
||||||
[:table {:columns 2}
|
|
||||||
[:strong "Input"] [:strong "Output"]
|
|
||||||
"1" "1"
|
|
||||||
"2" "4"
|
|
||||||
"3" "9"]])
|
|
||||||
```
|
|
||||||
|
|
||||||
**Compiles to:**
|
|
||||||
|
|
||||||
```typst
|
|
||||||
#heading(level: 1)[My Paper]
|
|
||||||
|
|
||||||
This is the introduction. We discuss #emph[important things].
|
|
||||||
|
|
||||||
#heading(level: 2)[Methods]
|
|
||||||
|
|
||||||
We used the following approach:
|
|
||||||
|
|
||||||
#list(
|
|
||||||
[Step one],
|
|
||||||
[Step two],
|
|
||||||
[Step three]
|
|
||||||
)
|
|
||||||
|
|
||||||
#heading(level: 2)[Results]
|
|
||||||
|
|
||||||
The result is $x = 42$.
|
|
||||||
|
|
||||||
#table(columns: 2,
|
|
||||||
[#strong[Input]], [#strong[Output]],
|
|
||||||
[1], [1],
|
|
||||||
[2], [4],
|
|
||||||
[3], [9]
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mixing Code and Content
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Define a reusable component
|
|
||||||
(defn author-block [name institution]
|
|
||||||
[:block {:align "center"}
|
|
||||||
[:strong name]
|
|
||||||
[:linebreak]
|
|
||||||
[:emph institution]])
|
|
||||||
|
|
||||||
;; Use it with data
|
|
||||||
(def authors
|
|
||||||
[{:name "Alice" :inst "MIT"}
|
|
||||||
{:name "Bob" :inst "Stanford"}])
|
|
||||||
|
|
||||||
;; Generate content programmatically
|
|
||||||
[[:heading "Authors"]
|
|
||||||
(for [{:keys [name inst]} authors]
|
|
||||||
(author-block name inst))]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Special Forms for Content
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Conditional content (strings become paragraphs naturally)
|
|
||||||
(if peer-reviewed?
|
|
||||||
"This paper was peer reviewed."
|
|
||||||
"Preprint - not yet reviewed.")
|
|
||||||
|
|
||||||
;; Loop over data (items are direct content in lists)
|
|
||||||
[:list
|
|
||||||
(for [item items]
|
|
||||||
(format-item item))]
|
|
||||||
|
|
||||||
;; Let bindings for intermediate content
|
|
||||||
(let [title (get-title data)
|
|
||||||
abstract (get-abstract data)]
|
|
||||||
[[:heading title]
|
|
||||||
abstract])
|
|
||||||
```
|
|
||||||
|
|
||||||
## The Alternative: Lisp for Code Only
|
|
||||||
|
|
||||||
If hiccup feels too heavy, we could just use Lisp for logic and escape to raw Typst:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(defn factorial [n]
|
|
||||||
(if (<= n 1)
|
|
||||||
1
|
|
||||||
(* n (factorial (dec n)))))
|
|
||||||
|
|
||||||
;; Escape to typst content
|
|
||||||
#t"
|
|
||||||
= Results
|
|
||||||
|
|
||||||
The factorial of 5 is ~(factorial 5).
|
|
||||||
|
|
||||||
Here's a table of factorials:
|
|
||||||
~(for [i (range 1 11)]
|
|
||||||
(str \"- \" i \"! = \" (factorial i) \"\\n\"))
|
|
||||||
"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Questions
|
|
||||||
|
|
||||||
1. **Implicit paragraphs?** Should adjacent strings auto-wrap in `[:p ...]`?
|
|
||||||
2. **Shorthand syntax?** Maybe `:#strong` instead of `[:strong ...]`?
|
|
||||||
3. **Component system?** How fancy do we get with reusable components?
|
|
||||||
4. **Escaping?** How to include literal `[` or other special chars?
|
|
||||||
5. **Whitespace handling?** Typst is whitespace-sensitive in content mode
|
|
||||||
|
|
||||||
## Verdict?
|
|
||||||
|
|
||||||
Hiccup gives us:
|
|
||||||
- ✅ Pure data representation of documents
|
|
||||||
- ✅ Full programmatic control
|
|
||||||
- ✅ Composable components
|
|
||||||
- ✅ Familiar to Clojure devs
|
|
||||||
|
|
||||||
But:
|
|
||||||
- ❌ More verbose than raw Typst markup
|
|
||||||
- ❌ Loses Typst's nice content syntax
|
|
||||||
- ❌ Another layer to learn
|
|
||||||
|
|
||||||
**Hybrid approach might be best:** Lisp for code/logic, with easy escape to Typst content syntax when you just want to write prose.
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
# Clojure-Typst: A Clojure-style Lisp for Typst
|
|
||||||
|
|
||||||
## The Problem
|
|
||||||
|
|
||||||
Typst is a modern typesetting system with a nice programming language, but it's not a Lisp. We only like Lisps.
|
|
||||||
|
|
||||||
## The Vision
|
|
||||||
|
|
||||||
Create a Clojure-inspired Lisp that compiles to Typst, bringing:
|
|
||||||
- S-expression syntax
|
|
||||||
- Functional programming idioms
|
|
||||||
- Macros and metaprogramming
|
|
||||||
- Immutable-first data structures
|
|
||||||
- REPL-driven development (maybe?)
|
|
||||||
|
|
||||||
## Open Questions
|
|
||||||
|
|
||||||
### Syntax & Semantics
|
|
||||||
- [ ] How closely do we follow Clojure syntax?
|
|
||||||
- [ ] What Typst constructs map to Lisp naturally?
|
|
||||||
- [ ] How do we handle Typst's content mode vs code mode distinction?
|
|
||||||
- [ ] Do we support Clojure-style destructuring?
|
|
||||||
- [ ] What about namespaces/modules?
|
|
||||||
|
|
||||||
### Data Structures
|
|
||||||
- [ ] Vectors `[]`, maps `{}`, sets `#{}`?
|
|
||||||
- [ ] How do these map to Typst arrays and dictionaries?
|
|
||||||
- [ ] Lazy sequences - possible in Typst?
|
|
||||||
|
|
||||||
### Interop
|
|
||||||
- [ ] How do we call Typst functions from our Lisp?
|
|
||||||
- [ ] How do we emit Typst content/markup?
|
|
||||||
- [ ] Can we import Typst packages?
|
|
||||||
|
|
||||||
### Implementation
|
|
||||||
- [ ] What language for the compiler? (Clojure? Rust? Typst itself?)
|
|
||||||
- [ ] AST representation
|
|
||||||
- [ ] Compilation strategy (source-to-source? interpreter?)
|
|
||||||
- [ ] Error messages and source maps
|
|
||||||
|
|
||||||
## Typst Features to Leverage
|
|
||||||
|
|
||||||
- Functions are first-class
|
|
||||||
- Has closures
|
|
||||||
- Pattern matching in function args
|
|
||||||
- Content as a first-class type
|
|
||||||
- Scripting mode `#` vs content mode
|
|
||||||
|
|
||||||
## Example Syntax Ideas
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
; Define a function
|
|
||||||
(defn greet [name]
|
|
||||||
(str "Hello, " name "!"))
|
|
||||||
|
|
||||||
; Emit typst content
|
|
||||||
(content
|
|
||||||
(heading "My Document")
|
|
||||||
(greet "World"))
|
|
||||||
|
|
||||||
; Maybe something like hiccup for content?
|
|
||||||
[:heading {:level 1} "My Document"]
|
|
||||||
["Hello, " [:strong "World"] "!"]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
1. Study Typst's language semantics more deeply
|
|
||||||
2. Identify the minimal viable feature set
|
|
||||||
3. Decide on implementation language
|
|
||||||
4. Build a basic parser for s-expressions
|
|
||||||
5. Implement core special forms (def, fn, let, if, do)
|
|
||||||
6. Add Typst content emission
|
|
||||||
7. Iterate
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
- [Typst Documentation](https://typst.app/docs)
|
|
||||||
- [Clojure Reference](https://clojure.org/reference)
|
|
||||||
- [Make a Lisp](https://github.com/kanaka/mal)
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
(Add brainstorming notes here)
|
|
||||||
@@ -1,425 +0,0 @@
|
|||||||
# Hiccup → Typst Mapping Reference
|
|
||||||
|
|
||||||
## Hiccup Syntax
|
|
||||||
|
|
||||||
All elements follow standard Hiccup:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
[:tag {attrs} children...]
|
|
||||||
```
|
|
||||||
|
|
||||||
- **tag**: Element type (keyword)
|
|
||||||
- **attrs**: Optional map of attributes
|
|
||||||
- **children**: Content (strings, nested elements, or expressions)
|
|
||||||
|
|
||||||
### Element Categories
|
|
||||||
|
|
||||||
| Category | Examples | Children |
|
|
||||||
|----------|----------|----------|
|
|
||||||
| Content elements | `heading`, `strong`, `emph`, `block` | Display content |
|
|
||||||
| Source elements | `link`, `image`, `bibliography` | `link` has display text; others have none |
|
|
||||||
| List elements | `list`, `enum`, `table` | Each child = one item/cell |
|
|
||||||
| Container elements | `figure` | Single content child (compiled as function arg) |
|
|
||||||
| Math | `math` | Math expression string |
|
|
||||||
| References | `ref`, `cite` | None |
|
|
||||||
| Code | `raw` | Code string to display |
|
|
||||||
| Rules | `set` | None |
|
|
||||||
| Show rules | `show` | A `set` rule as child |
|
|
||||||
|
|
||||||
### Reserved Attributes
|
|
||||||
|
|
||||||
| Attr | Used by | Purpose |
|
|
||||||
|------|---------|---------|
|
|
||||||
| `:src` | `link`, `image`, `bibliography` | Source URL/path |
|
|
||||||
| `:label` | Any element | Makes element referenceable |
|
|
||||||
| `:caption` | `figure` | Figure caption text |
|
|
||||||
| `:target` | `ref` | Label to reference |
|
|
||||||
| `:keys` | `cite` | Citation key(s) |
|
|
||||||
| `:element` | `set`, `show` | Element type to configure |
|
|
||||||
| `:block` | `math`, `raw` | Display as block (default: inline) |
|
|
||||||
| `:lang` | `raw` | Code language for syntax highlighting |
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Content element
|
|
||||||
[:heading {:level 2} "Section Title"]
|
|
||||||
|
|
||||||
;; Source element with children
|
|
||||||
[:link {:src "https://example.com"} "Click here"]
|
|
||||||
|
|
||||||
;; Source element without children
|
|
||||||
[:image {:src "diagram.png" :width "80%"}]
|
|
||||||
|
|
||||||
;; List element
|
|
||||||
[:list "First" "Second" "Third"]
|
|
||||||
|
|
||||||
;; Container with label
|
|
||||||
[:figure {:caption "Results" :label :fig/results}
|
|
||||||
[:image {:src "chart.png"}]]
|
|
||||||
|
|
||||||
;; Math (inline and block)
|
|
||||||
[:math "E = m c^2"]
|
|
||||||
[:math {:block true} "sum_(i=0)^n i"]
|
|
||||||
|
|
||||||
;; Reference and citation
|
|
||||||
[:ref {:target :fig/results}]
|
|
||||||
[:cite {:keys [:smith2020 :jones2021]}]
|
|
||||||
|
|
||||||
;; Code display
|
|
||||||
[:raw {:lang "python" :block true} "print('hello')"]
|
|
||||||
|
|
||||||
;; Set rule
|
|
||||||
[:set {:element :page :margin "2cm"}]
|
|
||||||
|
|
||||||
;; Show rule (child is the set rule to apply)
|
|
||||||
[:show {:element :heading}
|
|
||||||
[:set {:element :text :fill "blue"}]]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Audit of Current Tags
|
|
||||||
|
|
||||||
### ✅ Valid - Direct Typst Functions
|
|
||||||
|
|
||||||
| Hiccup | Typst | Notes |
|
|
||||||
|--------|-------|-------|
|
|
||||||
| `[:heading {:level 2} "Text"]` | `#heading(level: 2)[Text]` | ✅ Works |
|
|
||||||
| `[:strong "text"]` | `#strong[text]` | ✅ Works |
|
|
||||||
| `[:emph "text"]` | `#emph[text]` | ✅ Works |
|
|
||||||
| `[:link {:src "url"} "text"]` | `#link("url")[text]` | ✅ Works |
|
|
||||||
| `[:image {:src "path.png" :width "50%"}]` | `#image("path.png", width: 50%)` | ✅ Works |
|
|
||||||
| `[:block {:inset "1em"} ...]` | `#block(inset: 1em)[...]` | ✅ Works |
|
|
||||||
| `[:bibliography {:src "refs.bib"}]` | `#bibliography("refs.bib")` | ✅ Works |
|
|
||||||
|
|
||||||
### ❌ Invalid - Don't Exist in Typst
|
|
||||||
|
|
||||||
| Bad Hiccup | Problem | Fix |
|
|
||||||
|------------|---------|-----|
|
|
||||||
| `[:p "text"]` | No paragraph function | Just use strings, paragraphs are implicit |
|
|
||||||
| `[:item "text"]` | Not a function | Items are args to `list()` |
|
|
||||||
| `[:cell "text"]` | Not a function | Cells are args to `table()` |
|
|
||||||
| `[:div ...]` | HTML, not Typst | Use `[:block ...]` |
|
|
||||||
| `[:span ...]` | HTML, not Typst | Just inline content, or `[:box ...]` |
|
|
||||||
| `[:aside ...]` | Doesn't exist | Use `[:block ...]` with styling |
|
|
||||||
| `[:section ...]` | Doesn't exist | Just use headings |
|
|
||||||
| `[:doc ...]` | Doesn't exist | Top-level is just content |
|
|
||||||
| `[:table-row ...]` | Doesn't exist | Tables are flat |
|
|
||||||
|
|
||||||
### ⚠️ Needs Adjustment
|
|
||||||
|
|
||||||
| Current | Problem | Correct |
|
|
||||||
|---------|---------|---------|
|
|
||||||
| `[:linebreak]` | Function name | `[:linebreak]` → `#linebreak()` ✅ actually works |
|
|
||||||
| `[:h-space "2em"]` | Wrong name | `[:h "2em"]` → `#h(2em)` |
|
|
||||||
| `[:figure [:image ...] [:caption ...]]` | Caption isn't a child | Caption goes in attrs |
|
|
||||||
| `[:code {:lang "py"} "..."]` | Function is `raw` | `[:raw {:lang "python"} "..."]` |
|
|
||||||
|
|
||||||
## Corrected Mappings
|
|
||||||
|
|
||||||
### Lists
|
|
||||||
|
|
||||||
Typst `list` takes content blocks as positional args, not `:item` children:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; OLD (wrong)
|
|
||||||
[:list
|
|
||||||
[:item "First"]
|
|
||||||
[:item "Second"]]
|
|
||||||
|
|
||||||
;; NEW (correct)
|
|
||||||
[:list "First" "Second"]
|
|
||||||
;; => #list([First], [Second])
|
|
||||||
|
|
||||||
;; With formatted items
|
|
||||||
[:list
|
|
||||||
[:strong "Important"]
|
|
||||||
"Regular item"]
|
|
||||||
;; => #list([#strong[Important]], [Regular item])
|
|
||||||
|
|
||||||
;; Nested
|
|
||||||
[:list
|
|
||||||
"Top level"
|
|
||||||
[:list "Nested 1" "Nested 2"]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst output:**
|
|
||||||
```typst
|
|
||||||
#list(
|
|
||||||
[First],
|
|
||||||
[Second],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tables
|
|
||||||
|
|
||||||
Same pattern - cells are positional args:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; OLD (wrong)
|
|
||||||
[:table {:columns 2}
|
|
||||||
[:cell "A"] [:cell "B"]
|
|
||||||
[:cell "1"] [:cell "2"]]
|
|
||||||
|
|
||||||
;; NEW (correct)
|
|
||||||
[:table {:columns 2}
|
|
||||||
"A" "B"
|
|
||||||
"1" "2"]
|
|
||||||
;; => #table(columns: 2, [A], [B], [1], [2])
|
|
||||||
|
|
||||||
;; With formatting
|
|
||||||
[:table {:columns 2}
|
|
||||||
[:strong "Header 1"] [:strong "Header 2"]
|
|
||||||
"Data 1" "Data 2"]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst output:**
|
|
||||||
```typst
|
|
||||||
#table(
|
|
||||||
columns: 2,
|
|
||||||
[#strong[Header 1]], [#strong[Header 2]],
|
|
||||||
[Data 1], [Data 2],
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Figures
|
|
||||||
|
|
||||||
Caption is a named parameter, not a child element:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; OLD (wrong)
|
|
||||||
[:figure
|
|
||||||
[:image "arch.png"]
|
|
||||||
[:caption "Architecture diagram"]]
|
|
||||||
|
|
||||||
;; NEW (correct)
|
|
||||||
[:figure {:caption "Architecture diagram"}
|
|
||||||
[:image {:src "arch.png"}]]
|
|
||||||
;; => #figure(image("arch.png"), caption: [Architecture diagram])
|
|
||||||
|
|
||||||
;; With label for referencing
|
|
||||||
[:figure {:caption "Architecture" :label :fig/arch}
|
|
||||||
[:image {:src "arch.png" :width "80%"}]]
|
|
||||||
;; => #figure(image("arch.png", width: 80%), caption: [Architecture]) <fig:arch>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst output:**
|
|
||||||
```typst
|
|
||||||
#figure(
|
|
||||||
image("arch.png", width: 80%),
|
|
||||||
caption: [Architecture],
|
|
||||||
) <fig:arch>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Math
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Inline math (default)
|
|
||||||
[:math "x^2 + y^2"]
|
|
||||||
;; => $x^2 + y^2$
|
|
||||||
|
|
||||||
;; Block/display math
|
|
||||||
[:math {:block true} "sum_(i=0)^n i"]
|
|
||||||
;; => $ sum_(i=0)^n i $
|
|
||||||
```
|
|
||||||
|
|
||||||
### Code/Raw
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Inline code
|
|
||||||
[:raw "let x = 1"]
|
|
||||||
;; => `let x = 1`
|
|
||||||
|
|
||||||
;; Code block with language
|
|
||||||
[:raw {:lang "python" :block true} "print('hello')"]
|
|
||||||
;; => ```python
|
|
||||||
;; print('hello')
|
|
||||||
;; ```
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst output:**
|
|
||||||
```typst
|
|
||||||
#raw("let x = 1")
|
|
||||||
#raw(lang: "python", block: true, "print('hello')")
|
|
||||||
```
|
|
||||||
|
|
||||||
### References and Citations
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Reference a label
|
|
||||||
[:ref {:target :fig/arch}]
|
|
||||||
;; => @fig:arch
|
|
||||||
|
|
||||||
;; Single citation
|
|
||||||
[:cite {:keys [:smith2020]}]
|
|
||||||
;; => @smith2020
|
|
||||||
|
|
||||||
;; Multiple citations
|
|
||||||
[:cite {:keys [:smith2020 :jones2021]}]
|
|
||||||
;; => @smith2020 @jones2021
|
|
||||||
```
|
|
||||||
|
|
||||||
### Horizontal/Vertical Space
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
[:h "2em"] ;; => #h(2em)
|
|
||||||
[:v "1em"] ;; => #v(1em)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Page/Document Settings
|
|
||||||
|
|
||||||
`set` and `show` rules configure document styling. Use `:element` attr to specify the target:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Set rules (affects everything after)
|
|
||||||
[:set {:element :page :paper "a4" :margin "2cm"}]
|
|
||||||
;; => #set page(paper: "a4", margin: 2cm)
|
|
||||||
|
|
||||||
[:set {:element :text :font "New Computer Modern" :size "11pt"}]
|
|
||||||
;; => #set text(font: "New Computer Modern", size: 11pt)
|
|
||||||
|
|
||||||
;; Show rules (child is the set rule to apply)
|
|
||||||
[:show {:element :heading}
|
|
||||||
[:set {:element :text :fill "blue"}]]
|
|
||||||
;; => #show heading: set text(fill: blue)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Paragraphs
|
|
||||||
|
|
||||||
There is no paragraph function. Paragraphs are created by blank lines:
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
;; Just strings, separated by [:parbreak] or blank content
|
|
||||||
"First paragraph."
|
|
||||||
[:parbreak]
|
|
||||||
"Second paragraph."
|
|
||||||
|
|
||||||
;; Or in raw mode, just write naturally
|
|
||||||
#t"
|
|
||||||
First paragraph.
|
|
||||||
|
|
||||||
Second paragraph.
|
|
||||||
"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Revised Full Example
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
(def my-doc
|
|
||||||
[;; Set rules at top
|
|
||||||
[:set {:element :page :paper "a4"}]
|
|
||||||
[:set {:element :text :font "Linux Libertine"}]
|
|
||||||
|
|
||||||
[:heading {:level 1} "My Paper"]
|
|
||||||
|
|
||||||
"This is the introduction. We discuss "
|
|
||||||
[:emph "important things"] "."
|
|
||||||
|
|
||||||
[:parbreak]
|
|
||||||
|
|
||||||
[:heading {:level 2} "Methods"]
|
|
||||||
|
|
||||||
"We used the following approach:"
|
|
||||||
|
|
||||||
[:list
|
|
||||||
"Step one"
|
|
||||||
"Step two"
|
|
||||||
[:strong "Step three (important)"]]
|
|
||||||
|
|
||||||
[:heading {:level 2} "Results"]
|
|
||||||
|
|
||||||
"See " [:ref {:target :fig/results}] " and " [:ref {:target :tab/data}] "."
|
|
||||||
|
|
||||||
[:figure {:caption "Results visualization" :label :fig/results}
|
|
||||||
[:image {:src "results.png" :width "80%"}]]
|
|
||||||
|
|
||||||
[:figure {:caption "Data table" :label :tab/data}
|
|
||||||
[:table {:columns 2}
|
|
||||||
[:strong "Input"] [:strong "Output"]
|
|
||||||
"1" "1"
|
|
||||||
"2" "4"
|
|
||||||
"3" "9"]]
|
|
||||||
|
|
||||||
"The equation is " [:math "E = m c^2"] "."
|
|
||||||
|
|
||||||
[:math {:block true} "integral_0^infinity e^(-x^2) dif x = sqrt(pi)/2"]
|
|
||||||
|
|
||||||
[:bibliography {:src "refs.bib"}]])
|
|
||||||
```
|
|
||||||
|
|
||||||
**Compiles to:**
|
|
||||||
|
|
||||||
```typst
|
|
||||||
#set page(paper: "a4")
|
|
||||||
#set text(font: "Linux Libertine")
|
|
||||||
|
|
||||||
#heading(level: 1)[My Paper]
|
|
||||||
|
|
||||||
This is the introduction. We discuss #emph[important things].
|
|
||||||
|
|
||||||
#heading(level: 2)[Methods]
|
|
||||||
|
|
||||||
We used the following approach:
|
|
||||||
|
|
||||||
#list(
|
|
||||||
[Step one],
|
|
||||||
[Step two],
|
|
||||||
[#strong[Step three (important)]],
|
|
||||||
)
|
|
||||||
|
|
||||||
#heading(level: 2)[Results]
|
|
||||||
|
|
||||||
See @fig:results and @tab:data.
|
|
||||||
|
|
||||||
#figure(
|
|
||||||
image("results.png", width: 80%),
|
|
||||||
caption: [Results visualization],
|
|
||||||
) <fig:results>
|
|
||||||
|
|
||||||
#figure(
|
|
||||||
table(
|
|
||||||
columns: 2,
|
|
||||||
[#strong[Input]], [#strong[Output]],
|
|
||||||
[1], [1],
|
|
||||||
[2], [4],
|
|
||||||
[3], [9],
|
|
||||||
),
|
|
||||||
caption: [Data table],
|
|
||||||
) <tab:data>
|
|
||||||
|
|
||||||
The equation is $E = m c^2$.
|
|
||||||
|
|
||||||
$ integral_0^infinity e^(-x^2) dif x = sqrt(pi)/2 $
|
|
||||||
|
|
||||||
#bibliography("refs.bib")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Typst Gotchas
|
|
||||||
|
|
||||||
Important Typst behaviors the compiler must handle:
|
|
||||||
|
|
||||||
1. **Math variable spacing**: In math mode, adjacent letters like `mc` are parsed as a single variable name. To represent multiplication, add spaces: `$m c^2$` not `$mc^2$`. The compiler should insert spaces between single-letter variables.
|
|
||||||
|
|
||||||
2. **Referenceable tables**: Tables cannot have labels directly attached. To make a table referenceable with `@label`, wrap it in a `#figure()`:
|
|
||||||
```typst
|
|
||||||
#figure(
|
|
||||||
table(...),
|
|
||||||
caption: [...],
|
|
||||||
) <label>
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Font availability**: Fonts like "Linux Libertine" may not be installed. The compiler should either bundle fonts or use fallbacks.
|
|
||||||
|
|
||||||
## Summary of Design Decisions
|
|
||||||
|
|
||||||
1. **Removed**: `:p`, `:item`, `:cell`, `:div`, `:span`, `:aside`, `:section`, `:doc`
|
|
||||||
2. **Lists/Tables**: Children are direct content, not wrapped in `:item`/`:cell`
|
|
||||||
3. **Figures**: Caption is an attribute; child element compiles as function argument (no `#`)
|
|
||||||
4. **Math**: Use `:math` with optional `:block` attr (not `:$`/`:$$`)
|
|
||||||
5. **Code**: Use `:raw` for displayed code; `#t"..."` for Typst passthrough
|
|
||||||
6. **Space**: Use `:h`/`:v` not `:h-space`
|
|
||||||
7. **References**: Use `[:ref {:target :label}]` and `[:cite {:keys [...]}]`
|
|
||||||
8. **Settings**: Use `[:set {:element :page ...}]` - attrs first with `:element`
|
|
||||||
9. **Paragraphs**: Use `:parbreak` or just content flow
|
|
||||||
10. **Referenceable tables**: Must wrap in `:figure` with `:label` attribute
|
|
||||||
@@ -1,531 +0,0 @@
|
|||||||
# Visualization & Diagrams
|
|
||||||
|
|
||||||
Three levels of visualization in Typst, all mappable to hiccup.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Built-in Shapes
|
|
||||||
|
|
||||||
Typst has basic shapes built-in. Simple and direct.
|
|
||||||
|
|
||||||
### Rectangle
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:rect {:width "2cm" :height "1cm" :fill "blue"} "Label"]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#rect(width: 2cm, height: 1cm, fill: blue)[Label]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Square
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:square {:size "1cm" :stroke "2pt + red"}]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#square(size: 1cm, stroke: 2pt + red)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Circle
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:circle {:radius "0.5cm" :fill "green"}]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#circle(radius: 0.5cm, fill: green)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ellipse
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:ellipse {:width "2cm" :height "1cm"}]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#ellipse(width: 2cm, height: 1cm)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Polygon
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:polygon {:fill "yellow"} [0 0] [1 0] [0.5 1]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#polygon(fill: yellow, (0pt, 0pt), (1pt, 0pt), (0.5pt, 1pt))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Line
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:line {:start [0 0] :end [100 50] :stroke "2pt"}]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#line(start: (0pt, 0pt), end: (100pt, 50pt), stroke: 2pt)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Path
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:path {:closed true :fill "blue"} [0 0] [10 0] [10 10] [0 10]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#path(closed: true, fill: blue, (0pt, 0pt), (10pt, 0pt), (10pt, 10pt), (0pt, 10pt))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Curve (Bézier)
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:curve
|
|
||||||
[:move [0 0]]
|
|
||||||
[:cubic [10 20] [30 20] [40 0]]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#curve(
|
|
||||||
curve.move((0pt, 0pt)),
|
|
||||||
curve.cubic((10pt, 20pt), (30pt, 20pt), (40pt, 0pt)),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. CeTZ - Canvas Drawing
|
|
||||||
|
|
||||||
For complex custom drawings. Like TikZ for LaTeX.
|
|
||||||
|
|
||||||
### Basic Canvas
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:cetz/canvas
|
|
||||||
[:cetz/line [0 0] [2 2]]
|
|
||||||
[:cetz/circle [1 1] {:radius 0.5}]
|
|
||||||
[:cetz/rect [0 0] [2 1] {:fill "blue"}]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#import "@preview/cetz:0.4.2"
|
|
||||||
|
|
||||||
#cetz.canvas({
|
|
||||||
import cetz.draw: *
|
|
||||||
|
|
||||||
line((0, 0), (2, 2))
|
|
||||||
circle((1, 1), radius: 0.5)
|
|
||||||
rect((0, 0), (2, 1), fill: blue)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Line with Multiple Points
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:cetz/line [0 0] [1 0] [1 1] [0 1] {:close true}]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
line((0, 0), (1, 0), (1, 1), (0, 1), close: true)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Arc
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:cetz/arc [0 0] {:start 0 :stop 90 :radius 1}]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
arc((0, 0), start: 0deg, stop: 90deg, radius: 1)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bezier Curve
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:cetz/bezier [0 0] [1 1] [0.5 2] [0.5 -1]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
bezier((0, 0), (1, 1), (0.5, 2), (0.5, -1))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Content Label in Canvas
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:cetz/content [1 1] "Label here"]
|
|
||||||
[:cetz/content [0 0] [:strong "Bold label"]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
content((1, 1), [Label here])
|
|
||||||
content((0, 0), [#strong[Bold label]])
|
|
||||||
```
|
|
||||||
|
|
||||||
### Architecture Boxes Example
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
(defn box-with-label [pos size label]
|
|
||||||
(let [[x y] pos
|
|
||||||
[w h] size
|
|
||||||
cx (+ x (/ w 2))
|
|
||||||
cy (+ y (/ h 2))]
|
|
||||||
(list
|
|
||||||
[:cetz/rect pos [(+ x w) (+ y h)] {:fill "lightblue" :stroke "black"}]
|
|
||||||
[:cetz/content [cx cy] label])))
|
|
||||||
|
|
||||||
[:cetz/canvas
|
|
||||||
(box-with-label [0 0] [3 2] "Frontend")
|
|
||||||
(box-with-label [5 0] [3 2] "Backend")
|
|
||||||
(box-with-label [10 0] [3 2] "Database")
|
|
||||||
[:cetz/line [3 1] [5 1] {:mark {:end ">"}}]
|
|
||||||
[:cetz/line [8 1] [10 1] {:mark {:end ">"}}]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#cetz.canvas({
|
|
||||||
import cetz.draw: *
|
|
||||||
|
|
||||||
// Frontend
|
|
||||||
rect((0, 0), (3, 2), fill: lightblue, stroke: black)
|
|
||||||
content((1.5, 1), [Frontend])
|
|
||||||
|
|
||||||
// Backend
|
|
||||||
rect((5, 0), (8, 2), fill: lightblue, stroke: black)
|
|
||||||
content((6.5, 1), [Backend])
|
|
||||||
|
|
||||||
// Database
|
|
||||||
rect((10, 0), (13, 2), fill: lightblue, stroke: black)
|
|
||||||
content((11.5, 1), [Database])
|
|
||||||
|
|
||||||
// Arrows
|
|
||||||
line((3, 1), (5, 1), mark: (end: ">"))
|
|
||||||
line((8, 1), (10, 1), mark: (end: ">"))
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Fletcher - Diagrams & Flowcharts
|
|
||||||
|
|
||||||
High-level diagram DSL. Best for architecture, flowcharts, state machines.
|
|
||||||
|
|
||||||
### Basic Diagram
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:fletcher/diagram {:node-stroke "1pt" :spacing "2em"}
|
|
||||||
[:node [0 0] "Start"]
|
|
||||||
[:edge "->"]
|
|
||||||
[:node [1 0] "Process"]
|
|
||||||
[:edge "->"]
|
|
||||||
[:node [2 0] "End"]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge
|
|
||||||
|
|
||||||
#diagram(
|
|
||||||
node-stroke: 1pt,
|
|
||||||
spacing: 2em,
|
|
||||||
node((0, 0), [Start]),
|
|
||||||
edge("-|>"),
|
|
||||||
node((1, 0), [Process]),
|
|
||||||
edge("-|>"),
|
|
||||||
node((2, 0), [End]),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Node Shapes
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:node [0 0] "Default"]
|
|
||||||
[:node [1 0] "Circle" {:shape :circle}]
|
|
||||||
[:node [2 0] "Diamond" {:shape :diamond}]
|
|
||||||
[:node [3 0] "Pill" {:shape :pill}]
|
|
||||||
[:node [4 0] "Hexagon" {:shape :hexagon}]
|
|
||||||
[:node [5 0] "Cylinder" {:shape :cylinder}]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
node((0, 0), [Default]),
|
|
||||||
node((1, 0), [Circle], shape: circle),
|
|
||||||
node((2, 0), [Diamond], shape: fletcher.shapes.diamond),
|
|
||||||
node((3, 0), [Pill], shape: fletcher.shapes.pill),
|
|
||||||
node((4, 0), [Hexagon], shape: fletcher.shapes.hexagon),
|
|
||||||
node((5, 0), [Cylinder], shape: fletcher.shapes.cylinder),
|
|
||||||
```
|
|
||||||
|
|
||||||
### Edge Styles
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:edge "->"] ; simple arrow
|
|
||||||
[:edge "->>"] ; double arrow
|
|
||||||
[:edge "--"] ; no arrow (line)
|
|
||||||
[:edge "<->"] ; bidirectional
|
|
||||||
[:edge "hook->"] ; hook arrow
|
|
||||||
[:edge "|->"] ; maps-to arrow
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
edge("-|>"),
|
|
||||||
edge("-|>|>"),
|
|
||||||
edge("--"),
|
|
||||||
edge("<|-|>"),
|
|
||||||
edge("hook-|>"),
|
|
||||||
edge("|-|>"),
|
|
||||||
```
|
|
||||||
|
|
||||||
### Directional Edges
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:edge "r" "->"] ; go right
|
|
||||||
[:edge "d" "->"] ; go down
|
|
||||||
[:edge "u" "->"] ; go up
|
|
||||||
[:edge "l" "->"] ; go left
|
|
||||||
[:edge "r,d" "->"] ; right then down
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
edge("r", "-|>"),
|
|
||||||
edge("d", "-|>"),
|
|
||||||
edge("u", "-|>"),
|
|
||||||
edge("l", "-|>"),
|
|
||||||
edge("r,d", "-|>"),
|
|
||||||
```
|
|
||||||
|
|
||||||
### Edge with Label
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:edge [0 0] [1 0] "->" {:label "HTTP" :label-pos 0.5}]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
edge((0, 0), (1, 0), "-|>", label: [HTTP], label-pos: 0.5),
|
|
||||||
```
|
|
||||||
|
|
||||||
### Curved Edge
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:edge [0 0] [1 0] "->" {:bend 20}]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
edge((0, 0), (1, 0), "-|>", bend: 20deg),
|
|
||||||
```
|
|
||||||
|
|
||||||
### Flowchart Example
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:fletcher/diagram {:node-stroke "1pt" :node-fill "white" :spacing "3em"}
|
|
||||||
;; Nodes
|
|
||||||
[:node [0 0] "Start" {:shape :pill}]
|
|
||||||
[:node [0 1] "Input Data"]
|
|
||||||
[:node [0 2] "Valid?" {:shape :diamond}]
|
|
||||||
[:node [1 2] "Error" {:fill "red.lighten(80%)"}]
|
|
||||||
[:node [0 3] "Process"]
|
|
||||||
[:node [0 4] "End" {:shape :pill}]
|
|
||||||
|
|
||||||
;; Edges
|
|
||||||
[:edge [0 0] [0 1] "->"]
|
|
||||||
[:edge [0 1] [0 2] "->"]
|
|
||||||
[:edge [0 2] [1 2] "->" {:label "no"}]
|
|
||||||
[:edge [0 2] [0 3] "->" {:label "yes"}]
|
|
||||||
[:edge [1 2] [0 1] "->" {:bend -40}]
|
|
||||||
[:edge [0 3] [0 4] "->"]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge
|
|
||||||
|
|
||||||
#diagram(
|
|
||||||
node-stroke: 1pt,
|
|
||||||
node-fill: white,
|
|
||||||
spacing: 3em,
|
|
||||||
|
|
||||||
// Nodes
|
|
||||||
node((0, 0), [Start], shape: fletcher.shapes.pill),
|
|
||||||
node((0, 1), [Input Data]),
|
|
||||||
node((0, 2), [Valid?], shape: fletcher.shapes.diamond),
|
|
||||||
node((1, 2), [Error], fill: red.lighten(80%)),
|
|
||||||
node((0, 3), [Process]),
|
|
||||||
node((0, 4), [End], shape: fletcher.shapes.pill),
|
|
||||||
|
|
||||||
// Edges
|
|
||||||
edge((0, 0), (0, 1), "-|>"),
|
|
||||||
edge((0, 1), (0, 2), "-|>"),
|
|
||||||
edge((0, 2), (1, 2), "-|>", label: [no]),
|
|
||||||
edge((0, 2), (0, 3), "-|>", label: [yes]),
|
|
||||||
edge((1, 2), (0, 1), "-|>", bend: -40deg),
|
|
||||||
edge((0, 3), (0, 4), "-|>"),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Architecture Diagram Example
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
[:fletcher/diagram {:spacing "4em" :node-stroke "1pt"}
|
|
||||||
;; Frontend layer
|
|
||||||
[:node [0 0] "Web App" {:fill "blue.lighten(80%)"}]
|
|
||||||
[:node [1 0] "Mobile App" {:fill "blue.lighten(80%)"}]
|
|
||||||
|
|
||||||
;; API Gateway
|
|
||||||
[:node [0.5 1] "API Gateway" {:shape :hexagon :fill "green.lighten(80%)"}]
|
|
||||||
|
|
||||||
;; Services
|
|
||||||
[:node [0 2] "Auth Service" {:shape :pill}]
|
|
||||||
[:node [1 2] "User Service" {:shape :pill}]
|
|
||||||
[:node [2 2] "Order Service" {:shape :pill}]
|
|
||||||
|
|
||||||
;; Data layer
|
|
||||||
[:node [0 3] "PostgreSQL" {:shape :cylinder}]
|
|
||||||
[:node [1 3] "Redis" {:shape :cylinder :fill "red.lighten(80%)"}]
|
|
||||||
[:node [2 3] "MongoDB" {:shape :cylinder :fill "green.lighten(80%)"}]
|
|
||||||
|
|
||||||
;; Connections
|
|
||||||
[:edge [0 0] [0.5 1] "->"]
|
|
||||||
[:edge [1 0] [0.5 1] "->"]
|
|
||||||
[:edge [0.5 1] [0 2] "->"]
|
|
||||||
[:edge [0.5 1] [1 2] "->"]
|
|
||||||
[:edge [0.5 1] [2 2] "->"]
|
|
||||||
[:edge [0 2] [0 3] "->"]
|
|
||||||
[:edge [1 2] [1 3] "->"]
|
|
||||||
[:edge [2 2] [2 3] "->"]]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge
|
|
||||||
|
|
||||||
#diagram(
|
|
||||||
spacing: 4em,
|
|
||||||
node-stroke: 1pt,
|
|
||||||
|
|
||||||
// Frontend layer
|
|
||||||
node((0, 0), [Web App], fill: blue.lighten(80%)),
|
|
||||||
node((1, 0), [Mobile App], fill: blue.lighten(80%)),
|
|
||||||
|
|
||||||
// API Gateway
|
|
||||||
node((0.5, 1), [API Gateway], shape: fletcher.shapes.hexagon, fill: green.lighten(80%)),
|
|
||||||
|
|
||||||
// Services
|
|
||||||
node((0, 2), [Auth Service], shape: fletcher.shapes.pill),
|
|
||||||
node((1, 2), [User Service], shape: fletcher.shapes.pill),
|
|
||||||
node((2, 2), [Order Service], shape: fletcher.shapes.pill),
|
|
||||||
|
|
||||||
// Data layer
|
|
||||||
node((0, 3), [PostgreSQL], shape: fletcher.shapes.cylinder),
|
|
||||||
node((1, 3), [Redis], shape: fletcher.shapes.cylinder, fill: red.lighten(80%)),
|
|
||||||
node((2, 3), [MongoDB], shape: fletcher.shapes.cylinder, fill: green.lighten(80%)),
|
|
||||||
|
|
||||||
// Connections
|
|
||||||
edge((0, 0), (0.5, 1), "-|>"),
|
|
||||||
edge((1, 0), (0.5, 1), "-|>"),
|
|
||||||
edge((0.5, 1), (0, 2), "-|>"),
|
|
||||||
edge((0.5, 1), (1, 2), "-|>"),
|
|
||||||
edge((0.5, 1), (2, 2), "-|>"),
|
|
||||||
edge((0, 2), (0, 3), "-|>"),
|
|
||||||
edge((1, 2), (1, 3), "-|>"),
|
|
||||||
edge((2, 2), (2, 3), "-|>"),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. DSL Helpers
|
|
||||||
|
|
||||||
Build higher-level abstractions on top.
|
|
||||||
|
|
||||||
**Clojure:**
|
|
||||||
```clojure
|
|
||||||
(defn service [id name & {:keys [x y color] :or {color "white"}}]
|
|
||||||
[:node [x y] name {:id id :shape :pill :fill color}])
|
|
||||||
|
|
||||||
(defn database [id name & {:keys [x y color] :or {color "gray.lighten(80%)"}}]
|
|
||||||
[:node [x y] name {:id id :shape :cylinder :fill color}])
|
|
||||||
|
|
||||||
(defn connects [from to & {:keys [label style] :or {style "->"}}]
|
|
||||||
[:edge from to style (when label {:label label})])
|
|
||||||
|
|
||||||
[:fletcher/diagram {:spacing "3em"}
|
|
||||||
(service :auth "Auth" :x 0 :y 0)
|
|
||||||
(service :users "Users" :x 1 :y 0)
|
|
||||||
(database :pg "PostgreSQL" :x 0 :y 1)
|
|
||||||
(database :redis "Redis" :x 1 :y 1 :color "red.lighten(80%)")
|
|
||||||
|
|
||||||
(connects :auth :pg)
|
|
||||||
(connects :users :redis :label "cache")]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Typst:**
|
|
||||||
```typst
|
|
||||||
#diagram(
|
|
||||||
spacing: 3em,
|
|
||||||
node((0, 0), [Auth], shape: fletcher.shapes.pill, fill: white),
|
|
||||||
node((1, 0), [Users], shape: fletcher.shapes.pill, fill: white),
|
|
||||||
node((0, 1), [PostgreSQL], shape: fletcher.shapes.cylinder, fill: gray.lighten(80%)),
|
|
||||||
node((1, 1), [Redis], shape: fletcher.shapes.cylinder, fill: red.lighten(80%)),
|
|
||||||
|
|
||||||
edge((0, 0), (0, 1), "-|>"),
|
|
||||||
edge((1, 0), (1, 1), "-|>", label: [cache]),
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
| Use Case | Tool | Hiccup Prefix |
|
|
||||||
|----------|------|---------------|
|
|
||||||
| Simple shapes in content | Built-in | `:rect`, `:circle`, `:line` |
|
|
||||||
| Custom drawings, illustrations | CeTZ | `:cetz/canvas`, `:cetz/line` |
|
|
||||||
| Flowcharts, architecture | Fletcher | `:fletcher/diagram`, `:node`, `:edge` |
|
|
||||||
|
|
||||||
## Sources
|
|
||||||
|
|
||||||
- [Typst Visualize Docs](https://typst.app/docs/reference/visualize/)
|
|
||||||
- [Fletcher Package](https://typst.app/universe/package/fletcher/)
|
|
||||||
- [CeTZ Package](https://typst.app/universe/package/cetz/)
|
|
||||||
- [CeTZ Documentation](https://cetz-package.github.io/)
|
|
||||||
Reference in New Issue
Block a user