feat: render Markdown, mathematics and Mermaid diagrams server-side
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
# Scriptorium
|
||||
|
||||
Scriptorium renders scientific documents server-side in Go: Markdown into
|
||||
HTML, TeX mathematics into MathML Core, and Mermaid flowchart and sequence
|
||||
diagrams into SVG. Three engines, each written from scratch in this
|
||||
repository on the Go standard library alone, producing markup the reader's
|
||||
browser already knows how to draw: no JavaScript runs on the page that reads
|
||||
the result.
|
||||
|
||||
## The strengths, each with its proof
|
||||
|
||||
**Complete Markdown.** The engine renders the full CommonMark 0.31.2
|
||||
grammar plus the GitHub Flavored Markdown extensions. Measured against the
|
||||
official suites: 649 of 652 CommonMark examples pass, and the three
|
||||
exceptions are bare URLs and email addresses that the GFM extended
|
||||
autolinks turn into links, the same trade GitHub's own reference
|
||||
implementation makes; all 23 extension examples of the GFM specification
|
||||
pass byte for byte. Footnotes and definition lists come on top, rendered
|
||||
in the shapes GitHub and Pandoc established.
|
||||
|
||||
**Complete mathematics.** Every one of the 391 mathematical symbols in the
|
||||
KaTeX coverage table maps to MathML Core, verified by a systematic diff
|
||||
against KaTeX's own source tables, together with fractions, radicals,
|
||||
scripts with movable limits, stretchy delimiters, the amsmath environments,
|
||||
accents, styles, colours, extensible arrows and bounded macros. Where a
|
||||
KaTeX-compatible renderer needs JavaScript in the reader's browser,
|
||||
scriptorium emits MathML Core that Chromium, Firefox and Safari draw
|
||||
natively.
|
||||
|
||||
**Deterministic.** The same input produces byte-identical output on every
|
||||
call, in every process, on every machine. Diagram layouts break ties by the
|
||||
order of appearance and never by map iteration; tests assert byte equality
|
||||
across repeated renders in all three engines. Output is safe to cache, to
|
||||
diff and to sign.
|
||||
|
||||
**Zero dependencies.** The Go standard library alone, direct and
|
||||
transitive. There is no `go.sum`, because there is nothing to sum: no
|
||||
parser to supply-chain, no renderer to version-pin, no JavaScript bundle to
|
||||
ship. Compiles wherever Go compiles.
|
||||
|
||||
**Honest about its edges.** A construct outside a mappable surface is never
|
||||
dropped and never guessed at: mathematics degrades in place, the unknown
|
||||
command standing as its verbatim source inside a marked element while the
|
||||
rest of the expression still renders, and a diagram type outside the two
|
||||
grammars is refused with an error naming the type and the source line.
|
||||
|
||||
**Ready for servers.** The engines hold no state between calls, so every
|
||||
function is safe for concurrent use. Rendering never fails on malformed
|
||||
input: Markdown and mathematics always produce output, and a diagram error
|
||||
tells the author the line to fix.
|
||||
|
||||
**No sanitisation, by contract.** The library renders trusted input and
|
||||
writes the output verbatim; whether the result may reach a given audience
|
||||
is the consumer's policy. A consumer of untrusted input keeps its own
|
||||
sanitiser and applies it to the rendered result.
|
||||
|
||||
## A taste
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"sourcedock.dev/petrbalvin/scriptorium"
|
||||
)
|
||||
|
||||
func main() {
|
||||
os.Stdout.Write(scriptorium.Render([]byte(
|
||||
"# Measured, not guessed\n\nA *claim* with [a source](/uri) and some `code`.\n")))
|
||||
os.Stdout.Write(scriptorium.RenderMathDisplay([]byte(`\sum_{i=1}^{n} \frac{1}{i^2}`)))
|
||||
svg, err := scriptorium.RenderDiagram([]byte("flowchart LR\n A[Input] --> B{Model} --> C[Result]\n"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Stdout.Write(svg)
|
||||
}
|
||||
```
|
||||
|
||||
The first call prints exactly:
|
||||
|
||||
```html
|
||||
<h1>Measured, not guessed</h1>
|
||||
<p>A <em>claim</em> with <a href="/uri">a source</a> and some <code>code</code>.</p>
|
||||
```
|
||||
|
||||
The second prints a complete MathML Core element, the summation limits
|
||||
above and below the operator the way display mathematics sets them:
|
||||
|
||||
```html
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><munderover><mo movablelimits="true">∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><mfrac><mn>1</mn><msup><mi>i</mi><mn>2</mn></msup></mfrac></math>
|
||||
```
|
||||
|
||||
The third returns a self-contained SVG document of the flowchart, every
|
||||
element positioned by the library's own layout in whole pixels: ranks by
|
||||
longest path, ordering by barycentre sweeps.
|
||||
|
||||
## The engines
|
||||
|
||||
### Markdown
|
||||
|
||||
CommonMark 0.31.2 in full, with the GFM extensions: tables with per column
|
||||
alignment, strikethrough, task lists and extended autolinks (`www.`,
|
||||
`http(s)://`, `ftp://` and bare email addresses, with the punctuation,
|
||||
parenthesis and entity trimming rules of the specification), plus footnotes
|
||||
and definition lists. Tables render in the exact shape of GitHub's
|
||||
reference implementation.
|
||||
|
||||
### Mathematics
|
||||
|
||||
The standard TeX command surface that maps to MathML Core, measured against
|
||||
the KaTeX coverage list: the Greek letters including the variants, the
|
||||
relation and operator tables including negations and the colon relations,
|
||||
the arrows including the extensible `\x` family, the big operators, the
|
||||
delimiters, and the letter-like symbols; the amsmath environments (`matrix`
|
||||
and every delimited variant, `cases`, `aligned`, `align`, `gather`,
|
||||
`equation`, `array`, `substack`); accents and over and under lines; the
|
||||
font commands; spacing; colours; and bounded `\newcommand`, `\def` and
|
||||
`\DeclareMathOperator` macros with expansion limits that turn a runaway
|
||||
definition into an honest error rather than a hang.
|
||||
|
||||
### Diagrams
|
||||
|
||||
The two Mermaid grammars the forge's documents actually use, a coverage
|
||||
measured across every repository on the forge. Flowchart (including the
|
||||
historical `graph` alias): every node shape, every edge kind with labels,
|
||||
branching, subgraphs with their own direction, classes and styles.
|
||||
SequenceDiagram: participants and actors, all arrow kinds, notes,
|
||||
activations, the `alt`/`opt`/`loop`/`par`/`critical`/`break` blocks,
|
||||
coloured regions, `autonumber` and dividers. Everything else is refused
|
||||
with a clear error naming the type.
|
||||
|
||||
## The API
|
||||
|
||||
| Function | Renders | Fails |
|
||||
|---|---|---|
|
||||
| `Render(source []byte) []byte` | Markdown to HTML | never |
|
||||
| `RenderMath(source []byte) []byte` | TeX to inline MathML Core | never |
|
||||
| `RenderMathDisplay(source []byte) []byte` | TeX to display MathML Core | never |
|
||||
| `RenderDiagram(source []byte) ([]byte, error)` | Mermaid to SVG | on an unsupported type or a malformed statement |
|
||||
| `SupportedDiagram(kind string) bool` | reports the Mermaid scope | |
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
go get sourcedock.dev/petrbalvin/scriptorium
|
||||
```
|
||||
|
||||
Requires Go 1.27.1 or newer.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [docs/API.md](docs/API.md): the surface in full, with the exact
|
||||
boundary of the mathematics surface and every command that degrades
|
||||
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): how the engines are built
|
||||
- [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md): the workflow and the recipes
|
||||
- [CHANGELOG.md](CHANGELOG.md): the user-visible history
|
||||
- [CONTRIBUTING.md](CONTRIBUTING.md): how to contribute
|
||||
- [SECURITY.md](SECURITY.md): how to report a vulnerability
|
||||
|
||||
## Development
|
||||
|
||||
```sh
|
||||
just build # compile every package
|
||||
just test # the test suite with the coverage floor, under a memory fence
|
||||
just fmt # format
|
||||
just gates # the definition of done: build, format, vet, tests, race
|
||||
```
|
||||
|
||||
## Licence
|
||||
|
||||
MIT. See [LICENSE](LICENSE).
|
||||
|
||||
Copyright © 2026 [Petr Balvín](https://petrbalvin.org)
|
||||
Reference in New Issue
Block a user