latexindent-web
May 6, 2026 · View on GitHub
Built with Claude Opus 4.7.
latexindent.pl (v4.0.1) running entirely in the browser via zeroperl (Perl 5.42 compiled to WebAssembly / WASI), with a CodeMirror 6 editor for input and output.
Why
latexindent is a 11k-LOC pure-Perl LaTeX formatter. zeroperl is an actively maintained Perl interpreter compiled to WASM. This project demonstrates that the two combine into a static webpage — no server, no install, just a tab.
Build
npm install
npm run prepare-vendor # clones latexindent.pl + downloads YAML::Tiny + applies patches
npm run dev # http://localhost:8765/
Production build:
npm run build # → dist/, deployable to any static host
How it works
scripts/prepare-vendor.shclones the upstream latexindent.pl repo, downloadsYAML::Tiny, applies the patches inpatches/, copies the result plus our overrides intopublic/app/, and writes afiles.txtmanifest of every.pl/.pm/.yamlfile.- At runtime, the page fetches each file from
public/app/and inserts it into zeroperl'sMemoryFileSystem. - The input pane is a CodeMirror 6 editor with
stexsyntax highlighting, tab-as-indent, and line wrapping. The output pane is a read-only CodeMirror editor with the same highlighting. - Click Run latexindent and the page writes the input editor's contents
to
/app/input.tex, invokesrunFile('/app/latexindent.pl', ['/app/input.tex']), captures stdout into the output editor, and routes stderr to the log pane.
Patches
zeroperl excludes a few XS modules that latexindent transitively depends on
(Storable, B). The patches in patches/ work around that by stripping
unused Encode imports, replacing :encoding(UTF-8) PerlIO layers with
:utf8, and stubbing one B-using helper in YAML::Tiny. See the diffs
themselves for details — every change is annotated with (zeroperl POC).
overrides/LatexIndent/UTF8CmdLineArgsFileOperation.pm is a full rewrite of
the CLI/encoding helper module — too divergent to keep as a patch — that
preserves the public API but drops Encode and Win32 dependencies.
overrides/File/HomeDir.pm is a 3-line stub returning /home, since
File::HomeDir isn't bundled with zeroperl and we don't have a real home
directory in the browser.
Tests
End-to-end Playwright tests live in tests/:
npm install # one-time, picks up @playwright/test
npm test # spins up vite, runs the spec
The config sets webServer.reuseExistingServer = true, so if you already have
npm run dev running on :8765 the test run will use it.
Limitations
- Cold load transfers ~25 MB of WASM. Subsequent loads hit cache.
- First Run takes ~25 seconds because latexindent compiles a lot of regex
in its
BEGINblocks. - The Perl interpreter is reused across
Runclicks, so latexindent's module-level globals (loaded settings, etc.) leak between runs. Toggling-lor editing the YAML and re-running may give surprising results because previous settings persist in memory. Reload the page to start from a clean state. -w/ overwrite, log files, and userindentconfig.yamlaren't wired up.- See
patches/latexindent.patchfor the full list of latexindent features that were trimmed (Encode-based filename handling, mainly Win32-relevant).
Licenses
- This project: MIT (see
LICENSE). - latexindent.pl (vendored at build time): GPL-3.0.
- YAML::Tiny (vendored at build time): Perl Artistic / GPL-1+.
- zeroperl runtime (npm dependency): Apache-2.0.