@simonklee/opentui-tex
v0.3.1
Published
TeX renderable with Unicode and native image backends for OpenTUI
Readme
OpenTUI TeX
OpenTUI TeX renders TeX math in a standard OpenTUI renderable tree:
import { TexRenderable, UnicodeTexBackend } from "@simonklee/opentui-tex";
container.add(
new TexRenderable(renderer, {
formula: String.raw`\sum_{i=1}^{n} i^2`,
display: true,
foreground: "#ffffff",
background: "#000000",
backend: new UnicodeTexBackend(),
}),
);The terminal shows selectable text cells:
n
∑ i²
i = 1One package supplies two output types:
@simonklee/opentui-texrenders formulas as semantic Unicode cells without loading the TeX native library.@simonklee/opentui-tex/nativerenders formulas as images (Kitty, Sixel, or block cells) through a prebuilt shared library.
Both backends run on Bun and Node. On Node, native rendering requires version 26.4 or newer and experimental foreign function interface (FFI) flags. See Images.
Install
For npm releases from 0.2.0 onward, install one package:
npm install @simonklee/opentui-texYou can also use bun add.
Like @opentui/core, the package uses optional dependencies to select a
prebuilt library for the host platform. You do not need a local native build.
Prebuilt libraries exist for x64 and arm64 on macOS, Windows, Linux glibc 2.17,
and Linux musl. On Linux, Bun 1.3.14 installs both libc variants. The loader
selects one at runtime.
@opentui/core is a required peer dependency, not a bundled copy. Use the same
version across your application and its OpenTUI bindings. This release requires
0.0.0-20260812-1d34234c. Compatibility with other Core versions is not yet
verified. npm installs required peers automatically.
The JavaScript package is MIT-licensed. The optional native binaries are GPL-3.0-only because they link ZigTeX. Each binary package includes its license notices. Before you redistribute native rendering, review those licenses.
@simonklee/opentui-tex-native-source contains the corresponding source at
the same version. The source package is not a runtime dependency.
To upgrade from the 0.1.0 GitHub tarballs, replace imports from
@simonklee/opentui-tex-native with @simonklee/opentui-tex/native. Then remove
the separate native dependency. The existing 0.1.0 release assets are unchanged.
TexRenderable
TexRenderable is the primary application component. It is a backend-neutral
BoxRenderable, not an image subclass. You must select a backend:
const formula = new TexRenderable(renderer, {
formula: String.raw`\frac{-b \pm \sqrt{b^2-4ac}}{2a}`,
display: true,
foreground: "#ffffff",
background: "#000000",
widthMax: 60,
heightMax: 12,
backend: new UnicodeTexBackend(),
fallback: "unicode",
});
container.add(formula);Options beyond BoxOptions:
formula(required): TeX source, at most 4,096 UTF-8 bytes.foreground,background(required): six-digit hexadecimal colors (#rrggbb).backend(required): theTexBackendthat renders the formula.display: display style instead of inline style. The default isfalse.widthMax,heightMax: output limits in cells. The defaults are 80 and 24.fallback: behavior after a failure. The default is"message".streaming: preview updates for incomplete input. The default isfalse.strict: reject unsupported Unicode commands and delimiters instead of showing their names as text. This check also applies to previews. The default isfalse.imageOptions: options for the installedImageRenderable.onError: receives backend errors.
Automatic width and height include borders and padding. Explicit dimensions set the outer box size. Unicode output clips to the available cells without wrapping mathematical rows.
When you assign to formula.formula, TexRenderable installs synchronous Unicode
output. With the built-in Unicode backend, this is the final result. Other
backends run next. A native result replaces the preview with an image in the
same TexRenderable.
formula.whenReady() tracks replacement requests. It resolves after
TexRenderable installs the latest result. formula.ready exposes the
current request. setColors() renders the formula again after a terminal
theme change. TexRenderable aborts requests for stale or destroyed nodes.
With streaming: true, only the synchronous Unicode preview changes. The
layout shows incomplete constructs at the end of the input as □ placeholders.
The fallback option controls behavior after a failure:
"unicode"keeps the semantic preview."retain"restores the previous successful backend result."message"displays an error. This is the default."throw"rejects readiness.
Share one backend across multiple TexRenderable instances.
A TexBackend transfers ownership of each image output to the receiver. The
receiver must dispose the image. TexRenderable manages all images that its
backend returns, including stale outputs and outputs that the "retain"
fallback keeps. Thus, most applications do not manage images directly.
React and Solid
Use the same registration pattern as other OpenTUI extension packages:
import { registerTex } from "@simonklee/opentui-tex/react"; // or @simonklee/opentui-tex/solid
registerTex();registerTex() registers a <tex> component. Its reactive props are
formula, streaming, display, foreground, and background. The
component passes the other TexRenderable options without changes:
<tex
formula={String.raw`e^{i\pi} + 1 = 0`}
foreground="#ffffff"
background="#000000"
backend={backend}
/>@opentui/react and @opentui/solid are optional peer dependencies. Install
the one that matches your renderer.
Backends
A TexBackend returns an owned NativeImage or semantic Unicode text.
The Unicode backend installs a TextRenderable. The native backend installs
an ImageRenderable.
Unicode cells
UnicodeTexBackend parses a bounded subset of TeX math into an abstract syntax
tree. It arranges fractions, roots, scripts, accents, aligned equations,
matrices, cases, and annotated braces as two-dimensional terminal cells. Arrays
support l, c, r, and vertical rules. Continued fractions accept [l] and
[r]. Font commands preserve bold and italic attributes or select Unicode
mathematical alphabets.
The output remains selectable terminal text. string-width measures the
output. The backend is stateless and synchronous, and renderSync is public.
It needs no cache or explicit destruction.
To distinguish supported math from source that should remain unrendered, use
strict: true:
const output = new UnicodeTexBackend().renderSync({
formula: String.raw`\mathbb{R} + \mathbf{x}`,
display: true,
foreground: "#ffffff",
background: "#000000",
widthMax: 80,
heightMax: 24,
strict: true,
signal: new AbortController().signal,
});The Unicode backend accepts at most 4,096 UTF-8 source bytes and limits each layout box to 16,384 cells before clipping. It does not compile LaTeX documents or load user-defined TeX packages.
Images
Import the native backend explicitly. The package root of
@simonklee/opentui-tex never loads native code. The platform shared library
loads during the first native render:
import { NativeTexBackend } from "@simonklee/opentui-tex/native";
const backend = new NativeTexBackend();On Linux musl, set OPENTUI_LIBC=musl before you start the application.
This setting makes OpenTUI and TeX select musl libraries. See
Native rendering for library overrides and standalone
executables.
MicroTeX parses a TeX-math dialect and computes glyph and rule positions. ZigTeX records Scalable Vector Graphics (SVG) paths for the embedded glyph outlines from Latin Modern Math. NanoSVG rasterizes only the SVG that ZigTeX generates. It does not rasterize arbitrary external SVG.
MicroTeX uses one process-wide font context. The native library initializes
this context once through texInit. It keeps the context for the process
lifetime.
The renderer supports only the main JavaScript thread. If you construct
NativeTexRenderer in a Worker, the constructor fails immediately.
Ownership rules for direct use:
- If you call
NativeTexRenderer.renderAsync()directly, you must dispose each returned image. Cached results use independent retained references. When you dispose one result, the other results stay valid. NativeImage.takeRaw()requires exclusive ownership. Before you call it, dispose all retained references. These include references that the renderer cache and renderables hold.
Custom backends
TexBackend is a public interface with one method:
interface TexBackend {
render(request: TexRenderRequest): Promise<TexRenderOutput>;
}The request contains formula, display, foreground, background,
widthMax, heightMax, an AbortSignal, and optional strict validation for
Unicode math. The output is either
{ kind: "image", image } or { kind: "unicode", text, columns, rows }.
Unicode output can also include spans, an array of { text, color?, bold?, italic? }
objects. The concatenated span text equals text. TexRenderable uses these
spans to preserve styling. Plain-text consumers can continue to read text.
Node
Native rendering requires Node 26.4 or newer. Start Node with these options:
node --permission --allow-fs-read=<application> --allow-ffi --experimental-ffi app.jsDevelopment
You can develop the package without the native toolchain.
bun run test
bun run typecheck
bun run build:packageNative builds require Zig 0.16.0 and curl. Use this command to test a native
build:
bun run test:nativeFor npm authentication, release checks, and publishing, see Releasing.
