A native desktop LaTeX editor written in Rust, built around two pieces of custom infrastructure:
-
A from-scratch pure-Rust LaTeX engine (aldutex, tracked here as a submodule). FluXTeX compiles through aldutex by default. An optional Tectonic CLI fallback handles constructs aldutex hasn't grown into yet, so the rendered PDF stays faithful while the engine matures. See
document/compiler.rs. -
Real-time collaborative editing on a CRDT + WebRTC stack. A small signaling server (
fluxtex-signal) brokers SDP and ICE between peers; once the WebRTC data channel is open, peers sync directly using cola operations. The demo also runs a shared-file watcher in parallel as a belt-and-suspenders path for single-machine verification.
Plus the usual editor surface: floem-based UI, custom LaTeX syntax highlighting against the editor's rope, multi-page PDF preview via pdfium, and a native macOS menu bar.
┌──────────────────┐
│ fluxtex-signal │ WebSocket relay; rooms keyed by
│ (rooms + relay) │ 6-char codes; forwards SDP/ICE.
└─────┬────────┬───┘
│ │
WS │ │ WS
▼ ▼
┌──────────┐ ┌──────────┐
│ Peer A │ │ Peer B │
│ fluxtex- │ │ fluxtex- │
│ app │ │ app │
└────┬─────┘ └────┬─────┘
└────────────┘
WebRTC DataChannel
(cola CRDT ops, snapshots)
Each peer:
┌──────────────┐ ┌────────────────────┐ ┌─────────────────┐
│ Floem editor │ ── │ DocumentBuffer │ ── │ Compile pipe: │
│ (rope-based)│ │ (cola Replica + │ │ aldutex first, │
│ │ │ String mirror) │ │ tectonic on │
└──────────────┘ └────────────────────┘ │ fallback. │
└────────┬────────┘
▼
┌─────────────────┐
│ pdfium-render → │
│ multi-page PNG │
│ preview │
└─────────────────┘
| Path | What's there |
|---|---|
crates/fluxtex-app |
Editor binary: floem UI, compile pipeline, syntax highlighting, PDF preview, collaboration glue, macOS menu bar |
crates/fluxtex-signal |
Signaling server (lib for the wire types, bin for the relay) |
vendor/aldutex |
Pure-Rust LaTeX engine, as a Git submodule and the primary compiler |
vendor/pdfium |
Pdfium headers + license; the .dylib is fetched out-of-band (see Setup) |
vendor/tectonic |
Optional fallback compiler location |
- Rust 1.78+
- macOS (the menu bar code uses
objc2-app-kit; the rest is portable) vendor/aldutexinitialized as a submodule (see below)
git clone --recurse-submodules https://github.com/danielsuit/fluxtex
cd fluxtexIf you've already cloned without --recurse-submodules:
git submodule update --init --recursiveThe Pdfium headers are vendored; the dylib isn't (it's ~7 MB and platform- specific). Fetch the macOS arm64 build:
curl -L -o /tmp/pdfium-mac-arm64.tgz \
https://github.com/bblanchon/pdfium-binaries/releases/download/chromium/7802/pdfium-mac-arm64.tgz
tar -xzf /tmp/pdfium-mac-arm64.tgz -C vendor/pdfiumdocument/pdf_render.rs
walks up from the executable looking for vendor/pdfium/lib/libpdfium.dylib,
falling back to bind_to_system_library.
cargo buildWhen aldutex declines a document, FluXTeX can hand it to Tectonic so the preview still renders. If you don't install Tectonic, aldutex-only documents still compile — you'll just see an error pane for anything aldutex can't handle.
To enable the fallback, install Tectonic via Homebrew:
brew install tectonic…or drop a binary at vendor/tectonic/bin/tectonic, or point
FLUXTEX_TECTONIC_BIN at one. Bundle lookup order is documented at the
bottom of this README.
The editor takes per-instance flags so two copies can run side-by-side:
cargo run -p fluxtex-app -- \
--signaling 127.0.0.1:9000 \
--autosave path/to/doc.tex \
--label "My Peer"A scripted launcher boots the signaling server and two app instances pointed at the same shared file:
./run-demo.shClick Host in one window, type the displayed 6-character code into the other window's Room code field, and click Join. The status pill turns green when the WebRTC data channel is established; edits flow either way through the CRDT.
The demo additionally watches the shared autosave path on disk, which is useful for sanity-checking edit propagation without involving the network stack.
vendor/aldutex is a Git submodule. To pull the latest revision, rebuild,
and verify it still drives the editor cleanly:
./update-aldutex.shcargo test -p fluxtex-app -p fluxtex-signalCovers the wire-format round-trip for SyncMessage (Insert/Delete/Snapshot),
the snapshot-seed-the-joiner round-trip end-to-end through the CRDT, and the
shape of the signaling JSON protocol.
cargo fmt -p fluxtex-app -p fluxtex-signal -- --check
cargo clippy -p fluxtex-app -p fluxtex-signal --all-targets --no-deps -- -D warnings(--no-deps skips clippy on the aldutex submodule, which has its own lint
budget I don't try to enforce here.)
When the dispatcher falls back to Tectonic,
document/compiler.rs locates
the binary and optional bundle in this order:
| Resource | Sources, in priority |
|---|---|
| Binary | FLUXTEX_TECTONIC_BIN, vendor/tectonic/bin/tectonic, vendor/tectonic/tectonic, tectonic on PATH |
| Bundle | FLUXTEX_TECTONIC_BUNDLE, vendor/tectonic/bundles/{default.zip,default.bundle,tectonic-default.bundle}, vendor/tectonic/default.bundle |
If no bundle is configured, Tectonic uses its default network bundle on the first fallback compile, then caches it.
MIT.