Skip to content

Latest commit

 

History

History
91 lines (71 loc) · 3.93 KB

File metadata and controls

91 lines (71 loc) · 3.93 KB

Architecture

The core functionality of this site is to take Typst math syntax as input and render it to an SVG in real-time. It does so using the typst compiler in WASM. Everything runs client-side.

It roughly follows the The Elm Architecture

┌─────────────────────────────────────────────────────────────┐
│                        Browser                              │
│  ┌─────────────┐      ┌─────────────┐      ┌─────────────┐  │
│  │   Elm App   │─────▶│  MsgBridge  │─────▶│  Rust/WASM  │  │
│  │  (elm.js)   │◀─────│ (index.html)│◀─────│   (pkg/)    │  │
│  └─────────────┘      └─────────────┘      └─────────────┘  │
│     UI State                                Typst Compiler  │
│    [compiled]                                 [compiled]    │
└─────────────────────────────────────────────────────────────┘

Components

Elm Application (src/ -> public/elm.js)

  • Main.elm - The Elm Architecture application managing UI state
  • Ports.elm - Port definitions for JavaScript interop

The Elm app handles:

  • User input (math expression text field)
  • UI rendering and styling
  • Sending render requests via renderMath port
  • Receiving SVG results via mathRendered port

JavaScript Msg Bridge (public/index.html)

The <script type="module"> block in index.html:

  1. Initializes the WASM module
  2. Starts the Elm app
  3. Subscribes to app.ports.renderMath to receive expressions from Elm
  4. Calls the WASM renderMath() function
  5. Injects the resulting SVG into the DOM
  6. Sends results back to Elm via app.ports.mathRendered

Rust/WASM Module (wasm/public/pkg/)

  • Cargo.toml - Dependencies on typst, typst-svg, typst-assets, wasm-bindgen
  • src/lib.rs - WASM-compatible Typst compiler

The Rust code:

  • Implements the typst::World trait (MathWorld) for WASM
  • Embeds fonts from typst-assets
  • Wraps math expressions in minimal Typst document markup
  • Compiles to a PagedDocument and renders to SVG via typst-svg
  • Exports renderMath(expression) -> Result<String, String> to JavaScript

Build Output (public/)

elm.js - Generated by elm make src/Main.elm --output=public/elm.js:

  • Elm runtime (curried functions, Virtual DOM, ports infrastructure)
  • Compiled application code from Main.elm and Ports.elm
  • All imported Elm packages (elm/html, elm/browser, elm/json)

pkg/ - Generated by wasm-pack build --target web:

  • typst_math_svg.js - ES module wrapper for the WASM
  • typst_math_svg_bg.wasm - Compiled WebAssembly binary
  • Type definitions and glue code

Data Flow

  1. User provides data in the input field
  2. Elm's onInput triggers ExpressionChanged message
  3. update sends expression through Ports.renderMath
  4. JS bridge receives via app.ports.renderMath.subscribe()
  5. JS calls WASM renderMath(expression)
  6. Rust compiles Typst → SVG
  7. JS injects SVG into #svg-output div
  8. JS sends SVG back via app.ports.mathRendered.send()
  9. Elm receives via Ports.mathRendered subscription

Build System

Nix Flake (flake.nix) provides:

  • Rust toolchain with wasm32-unknown-unknown target
  • Elm compiler and tools
  • wasm-pack, wasm-bindgen-cli, binaryen
  • Bun and Node.js

Makefile targets:

  • make build - Build WASM and Elm
  • make dev - Build and serve on port 8000, using bunx
  • make deploy - Push public/ to gh-pages branch
  • make clean - Remove build artifacts