This is a special post for quick takes by Johannes C. Mayer. Only they can create top-level comments. Comments here also appear on the Quick Takes page and All Posts page.
I started to use Typst. I feel a lot more productive in it. Latex feels like a slug. Typst doesn't feel like it slows me down when typing math, or code. That and the fact that it has an online collaborative editor, and that rendering is very very fast are the most important features. Here are some more:
Here is a comparison of encoding the games of life in logic:
Latex
$$
\forall i, j \in \mathbb{Z}, A_{t+1}(i, j) = \begin{cases}
0 &\text{if} \quad A_t(i, j) = 1 \land N_t(i, j) < 2 \\
1 &\text{if} \quad A_t(i, j) = 1 \land N_t(i, j) \in \{2, 3\} \\
0 &\text{if} \quad A_t(i, j) = 1 \land N_t(i, j) > 3 \\
1 &\text{if} \quad A_t(i, j) = 0 \land N_t(i, j) = 3 \\
0 &\text{otherwise}
\end{cases}
$$
Typst
$
forall i, j in ZZ, A_(t+1)(i, j) = cases(
0 "if" A_t(i, j) = 1 and N_t(i, j) < 2 \
1 "if" A_t(i, j) = 1 and N_t(i, j) in {2, 3} \
0 "if" A_t(i, j) = 1 and N_t(i, j) > 3 \
1 "if" A_t(i, j) = 0 and N_t(i, j) = 3 \
0 "otherwise")
$
Here is some elisp to treat latex blocks in emacs org-mode as typst math, when exporting to HTML (renders/embeds as SVG images):
;;;; Typst Exporter
;;; This exporter requires that you have inkscape and typst in your path.
;;; Call org-typst-enabled-html-export
;;; TODO
;;; - Error if inskape or typst is not installed.
;;; - Make it such that it shows up in the org-dispatch exporter and we can
;;; automatically not export only to output.html.
;;; - Automatically setup the HTML header, and possible also automatically start the server as described in: [[id:d9f72e91-7e8d-426d-af46-037378bc9b15][Setting up org-typst-html-exporter]]
;;; - Make it such that the temporary buffers are deleted after use.
(require 'org)
(require 'ox-html) ; Make sure the HTML backend is loaded
(defun spawn-trim-svg (svg-file-path output-file-path)
(start-process svg-file-path
nil
"inkscape"
svg-file-path
"--export-area-drawing"
"--export-plain-svg"
(format "--export-filename=%s" output-file-path)))
(defun correct-dollar-sings (typst-src)
(replace-regexp-in-string "\\$\\$$"
" $" ; Replace inital $$ with '$ '
(replace-regexp-in-string "^\\$\\$" "$ " ; same for ending $$
typst-src)))
(defun math-block-p (typst-src)
(string-match "^\\$\\$\\(\\(?:.\\|\n\\)*?\\)\\$\\$$" typst-src))
(defun html-image-centered (image-path)
(format "<div style=\"display: flex; justify-content: center; align-items: center;\">\n<img src=\"%s\" alt=\"Centered Image\">\n</div>" image-path))
(defun html-image-inline (image-path)
(format " <img hspace=3px src=\"%s\"> " image-path))
(defun spawn-render-typst (file-format input-file output-file)
(start-process input-file nil "typst" "compile" "-f" file-format input-file output-file))
(defun generate-typst-buffer (typst-source)
"Given typst-source code, make a buffer with this code and neccesary preamble."
(let ((buffer (generate-new-buffer (generate-new-buffer-name "tmp-typst-source-buffer"))))
(with-current-buffer buffer
(insert "#set text(16pt)\n")
(insert "#show math.equation: set text(14pt)\n")
(insert "#set page(width: auto, height: auto)\n")1
(insert typst-source))
buffer))
(defun embed-math (is-math-block typst-image-path)
(if is-math-block
(html-image-centered typst-image-path)
(html-image-inline typst-image-path)))
(defun generate-math-image (output-path typst-source-file)
(let* ((raw-typst-render-output (make-temp-file "my-temp-file-2" nil ".typ")))
(spawn-render-typst file-format typst-source-file raw-typst-render-output)
(spawn-trim-svg raw-typst-render-output typst-image-path)))
(defun my-typst-math (latex-fragment contents info)
;; Extract LaTeX source from the fragment's plist
(let* ((typst-source-raw (org-element-property :value latex-fragment))
(is-math-block (math-block-p typst-source-raw))
(typst-source (correct-dollar-sings typst-source-raw))
(file-format "svg") ;; This is the only supported format.
(typst-image-dir (concat "./typst-svg"))
(typst-buffer (generate-typst-buffer typst-source)) ; buffer of full typst code to render
(typst-source-file (make-temp-file "my-temp-file-1" nil ".typ"))
;; Name is unique for every typst source we render to enable caching.
(typst-image-path (concat typst-image-dir "/"
(secure-hash 'sha256 (with-current-buffer typst-buffer (buffer-string)))
"." file-format)))
;; Only render if neccesary
(unless (file-exists-p typst-image-path)
(message (format "Rendering: %s" typst-source))
;; Write the typst code to a file
(with-current-buffer typst-buffer
(write-region (point-min) (point-max) typst-source-file))
(generate-math-image typst-image-path typst-source-file))
(kill-buffer typst-buffer)
(embed-math is-math-block typst-image-path)))
(org-export-define-derived-backend 'my-html 'html
:translate-alist '((latex-fragment . my-typst-math))
:menu-entry
'(?M "Export to My HTML"
((?h "To HTML file" org-html-export-to-html))))
;; Ensure org-html-export-to-html is bound correctly to your backend:
(defun org-html-export-to-html-with-typst (&optional async subtreep visible-only body-only ext-plist)
(interactive)
(let* ((buffer-file-name (buffer-file-name (window-buffer (minibuffer-selected-window))))
(html-output-name (concat (file-name-sans-extension buffer-file-name) ".html")))
(org-export-to-file 'my-html html-output-name
async subtreep visible-only body-only ext-plist)))
(setq org-export-backends (remove 'html org-export-backends))
(add-to-list 'org-export-backends 'my-html)
Simply eval this code and then call org-html-export-to-html-with-typst.
Thanks for the rec! I've been trying it out for the last few days, and it does seem to have noticeably less friction compared to LaTeX.