Skip to content

Repository files navigation

Pinax.jl

docs: dev Julia Code Style: Blue

codecov Build Status Aqua QA License

A board of figures that is also a catalogue. πίναξ (Ancient Greek) — "tablet / catalogue / register"; the Pinakes were the catalogue of the Library of Alexandria.

Pinax turns the figures and tables your analysis scripts produce into a structured, self-contained catalogue of a computational study — described once and rendered three ways:

  • HTML gallery — sections as cards, a responsive figure grid, markdown + KaTeX math, cross-references, citations, and an interactive comment layer;
  • LaTeX → PDF — the same manuscript as a typeset document;
  • agent.json — a machine-readable view (figures as data tables, sections, captions) that an LLM or downstream tool can read.

It generalizes the hand-written build_report page an analysis pipeline grows over time: you describe the manuscript once with a small DSL, point each figure at the value (or data key) it plots, and render writes the artifact.

Installation

pkg> add Pinax

Requires Julia v1.12+.

Quickstart

using Pinax, Plots

plot_energy() = plot(0.1:0.05:2, β -> 1 / β; xlabel = "β", ylabel = "E/N", legend = false)

@page :results "Results" begin
    @section :energy "Energy" begin
        @desc md"Energy density $E/N$ versus inverse temperature $\beta$."
        @figure plot_energy()      # any Plots/Makie figure — captured lazily
        @caption "χ-convergence"
    end
end

render(out = "site")               # -> site/index.html + assets/figures/…/energy_fig1.svg

Then preview it, or render the same document another way:

serve("site")                             # preview over HTTP
render(theme = :latex, out = "pdf")       # -> pdf/document.tex  -> PDF
render(theme = :agent, out = "agent")     # -> agent/agent.json  (machine-readable)

@figure captures its expression lazily, so figures are computed (and cached) only when you render. Sections become cards; figures lay out in a responsive grid; $…$ math is rendered by KaTeX.

Three faces of one report

theme output for
:gallery (default) self-contained HTML humans browsing results
:latex LaTeX → PDF manuscripts / sharing
:agent agent.json LLMs / downstream tooling

The same source — sections, @desc/@caption, @table, citations, a @benchmark's PASS/FAIL verdict — flows to every face. Themes are pluggable: render(; theme = MyTheme()), register_theme!(:mine, MyTheme()), or theme = "path/to/mytheme.jl".

Bridging a parameter sweep

report(vault, recipe; title, out) discovers a finished sweep's results, hands each (key, dict) pair to a project-specific recipe that builds the doc, and renders both the gallery and agent.json — so the same results become a human notebook and an LLM-readable artifact in one call.

Bridging a test suite

This is an experimental feature. On Julia 1.13+ the Pkg.test delegation (Pinax.test() with no argument) is unavailable: it installs a root testset that has to survive into the suite the preamble runs afterwards, and 1.13 moved Test's testset stack to a ScopedValue, which cannot outlive the block that sets it. It warns once and produces no report; the suite runs and its verdict is unchanged. Pinax.test("test/runtests.jl") is unaffected: it wraps the suite in a @testset, which needs no such thing.

A test suite reports one bit: green or red. A @test isapprox(E, oracle; rtol=1e-3) computed E, the reference and the tolerance, then threw all three away. Pinax.test renders the suite instead — one page per test file, each check carrying the margin it passed by.

The suite is not touched. There is no Pinax macro to add to it; the suite stays plain @testset / @test, and the one Pinax touch is the call:

using Pinax, Test

Pinax.test()                     # delegate to an unmodified `Pkg.test`, and render a report
Pinax.test("test/runtests.jl")   # render a specific suite in the current process

Pinax.test() injects a -L preamble that installs a capturing root before the suite runs and renders at exit; Pkg.test still does all the sandbox and dependency work. A bare Pkg.test() installs no root and produces no report, so switching the report on cannot regress a passing suite. A red suite always fails the process — a report must never turn a failing suite green.

A test file becomes a page, a nested @testset a section, and each @test a Check with its real got / want / tol. @pinaxignore drops a testset from the document while still running and counting it.

Sharded CI uses the same primitive: with PINAX_TEST_DUMP a run dumps its testset tree as TOML instead of rendering, and render_test_report(dumps; out) merges the dumps into one gallery whose pages are the test files — the shard boundary never appears in the output. Publishing that report alongside the Documenter docs is #68.

MCP server

render(theme = :agent) emits an agent.json an LLM can read. clients/pinax-mcp is a Node MCP server over that artifact: it serves every unit (figure / table / section) by id and presents a figure as its underlying data table — npx pinax-mcp --agent <render-out>. See its README.

About

Describe a computational study's figures and tables once and render it three ways: a self-contained HTML gallery with KaTeX math and a comment layer, a LaTeX → PDF manuscript, and a machine-readable agent.json.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages