Class: Pdf::Typst

Inherits:
Object
  • Object
show all
Defined in:
app/services/pdf/typst.rb

Overview

Compiles Typst markup (https://typst.app) into a PDF. This is Sunny's
preferred AUTHORING path for new documents: the LLM writes plain-text .typ
source (git-diffable, re-renderable, translatable at the source level)
instead of a declarative hexapdf layout — and the source is stored on the
upload (+uploads.typst_source+) so future edits/translations recompile
instead of overlay-patching a rendered PDF.

ONE code path in dev and prod: the typst compile SERVICE
(TYPST_SERVER_URL) — an opt-in docker-compose profile in development
(docker compose --profile typst up -d typst → http://localhost:8002) and
the typst Kamal accessory in production/staging (config/deploy*.yml).
Both run the same self-built image (docker/typst.Dockerfile) with the
brand assets baked in, so the HTTP path under test locally is the shipped
one. A local binary (brew typst, or bin/typst-docker) is an OFFLINE
FALLBACK only. When neither exists, Typst.compile raises NotConfigured.

Documents import the brand prelude for chrome/fonts/helpers:
#import "/data/typst/warmly.typ": warmly-doc, spec-table, checklist
#show: warmly-doc.with(title: "…", subtitle: "…")

Examples:

result = Pdf::Typst.compile("#import \"/data/typst/warmly.typ\": *\n= Hi")
result.bytes # => "%PDF-1.7…"

Defined Under Namespace

Classes: Error, NotConfigured, Result

Constant Summary collapse

COMPILE_TIMEOUT =

Wall-clock cap for one compile. Typst is fast (~50ms incremental, <2s
cold for manuals); the cap exists for pathological inputs.

60

Class Method Summary collapse

Class Method Details

.binaryString

Returns path/name of the typst executable.

Returns:

  • (String)

    path/name of the typst executable



48
49
50
# File 'app/services/pdf/typst.rb', line 48

def binary
  ENV['TYPST_BINARY'].presence || 'typst'
end

.compile(source) ⇒ Result

Compile Typst markup to PDF. Prefers the typst SERVICE
(TYPST_SERVER_URL — compose in dev, Kamal accessory in prod, so the
tested HTTP path is the shipped one); falls back to a local binary
(brew / bin/typst-docker) for offline work.

Parameters:

  • source (String)

    the .typ document source

Returns:

  • (Result)

    bytes + meta(:pages, :source_chars)

Raises:

  • (NotConfigured)

    when neither the service nor a binary exists

  • (Error)

    with typst's diagnostics on compile failure



66
67
68
69
70
71
# File 'app/services/pdf/typst.rb', line 66

def compile(source)
  raise Error, 'source must be present' if source.to_s.strip.empty?
  raise NotConfigured, 'no typst compile path — set TYPST_SERVER_URL (compose profile / accessory) or install the binary (brew / bin/typst-docker)' unless configured?

  server_url.present? ? compile_via_server(source) : compile_via_binary(source)
end

.configured?Boolean

Returns whether a compile path is configured (service or binary).

Returns:

  • (Boolean)

    whether a compile path is configured (service or binary)



53
54
55
# File 'app/services/pdf/typst.rb', line 53

def configured?
  server_url.present? || binary_available?
end