426 lines
10 KiB
Markdown
426 lines
10 KiB
Markdown
# 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
|