@simonklee/opentui-tex 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenTUI Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,238 @@
1
+ # OpenTUI TeX
2
+
3
+ OpenTUI TeX renders TeX math in an ordinary renderable tree from
4
+ [OpenTUI](https://github.com/anomalyco/opentui):
5
+
6
+ ```ts
7
+ import { TexRenderable, UnicodeTexBackend } from "@simonklee/opentui-tex";
8
+
9
+ container.add(
10
+ new TexRenderable(renderer, {
11
+ formula: String.raw`\sum_{i=1}^{n} i^2`,
12
+ display: true,
13
+ foreground: "#ffffff",
14
+ background: "#000000",
15
+ backend: new UnicodeTexBackend(),
16
+ }),
17
+ );
18
+ ```
19
+
20
+ The terminal shows selectable text cells:
21
+
22
+ ```text
23
+ n
24
+ ∑ i²
25
+ i = 1
26
+ ```
27
+
28
+ One package supplies two output types:
29
+
30
+ - `@simonklee/opentui-tex` renders formulas as semantic Unicode cells without
31
+ loading the TeX native library.
32
+ - `@simonklee/opentui-tex/native` renders formulas as images (Kitty, Sixel, or
33
+ block cells) through a prebuilt shared library.
34
+
35
+ Both backends run on Bun and Node. Native rendering requires Node 26.4 or
36
+ newer and experimental FFI flags. See [Images](#images).
37
+
38
+ ## Install
39
+
40
+ Starting with the npm release of 0.2.0, install one package:
41
+
42
+ ```sh
43
+ npm install @simonklee/opentui-tex
44
+ ```
45
+
46
+ You can also use `bun add`. Like `@opentui/core`, the package uses optional
47
+ dependencies to select a prebuilt library for the host platform. No local
48
+ native build is required. Prebuilt libraries exist for
49
+ x64 and arm64 on macOS, Windows, Linux glibc 2.17, and Linux musl.
50
+ On Linux, Bun 1.3.14 installs both libc variants; the loader selects one at runtime.
51
+
52
+ `@opentui/core` is a required peer dependency, not a bundled copy. Use the same
53
+ version across your application and its OpenTUI bindings. This release requires
54
+ `0.0.0-20260812-1d34234c`; compatibility with other Core versions is not yet
55
+ verified. npm installs required peers automatically.
56
+
57
+ The JavaScript package is MIT-licensed. The optional native binaries are
58
+ GPL-3.0-only because they link ZigTeX. Each binary package includes its license
59
+ notices, and `@simonklee/opentui-tex-native-source` contains the corresponding
60
+ source at the same version. Review those licenses before redistributing native
61
+ rendering. The source package is not a runtime dependency.
62
+
63
+ For applications upgrading from the 0.1.0 GitHub tarballs, replace imports from
64
+ `@simonklee/opentui-tex-native` with `@simonklee/opentui-tex/native` and remove
65
+ the separate native dependency. The existing 0.1.0 release assets are unchanged.
66
+
67
+ ## TexRenderable
68
+
69
+ `TexRenderable` is the primary application component. It is a backend-neutral
70
+ `BoxRenderable`, not an image subclass. You must select a backend:
71
+
72
+ ```ts
73
+ const formula = new TexRenderable(renderer, {
74
+ formula: String.raw`\frac{-b \pm \sqrt{b^2-4ac}}{2a}`,
75
+ display: true,
76
+ foreground: "#ffffff",
77
+ background: "#000000",
78
+ widthMax: 60,
79
+ heightMax: 12,
80
+ backend: new UnicodeTexBackend(),
81
+ fallback: "unicode",
82
+ });
83
+
84
+ container.add(formula);
85
+ ```
86
+
87
+ Options beyond `BoxOptions`:
88
+
89
+ - `formula` (required): TeX source, at most 4,096 UTF-8 bytes.
90
+ - `foreground`, `background` (required): six-digit hex colors (`#rrggbb`).
91
+ - `backend` (required): the `TexBackend` that renders the formula.
92
+ - `display`: display style instead of inline style. The default is `false`.
93
+ - `widthMax`, `heightMax`: output limits in cells. The defaults are 80 and 24.
94
+ - `fallback`: behavior after a failure. The default is `"message"`.
95
+ - `streaming`: preview updates for incomplete input. The default is `false`.
96
+ - `imageOptions`: options for the installed `ImageRenderable`.
97
+ - `onError`: receives backend errors.
98
+
99
+ When you assign `formula.formula`, `TexRenderable` installs a synchronous
100
+ Unicode preview. It then starts a request for the selected backend. A native
101
+ result replaces the preview with an image in the same `TexRenderable`.
102
+
103
+ `formula.whenReady()` tracks replacement requests. It resolves after
104
+ `TexRenderable` installs the latest result. `formula.ready` exposes the
105
+ current request. `setColors()` renders the formula again after a terminal
106
+ theme change. `TexRenderable` aborts requests for stale or destroyed nodes.
107
+
108
+ With `streaming: true`, only the synchronous Unicode preview changes. The
109
+ layout shows incomplete constructs at the end as `□` placeholders.
110
+
111
+ The `fallback` option controls behavior after a failure. `"unicode"` keeps the
112
+ semantic preview. `"retain"` restores the previous successful backend result.
113
+ `"message"` displays an error. `"throw"` rejects readiness. The default is
114
+ `"message"`.
115
+
116
+ Share one backend across multiple `TexRenderable` instances. A `TexBackend`
117
+ transfers ownership of each image output to the receiver. The receiver must
118
+ dispose the image. `TexRenderable` manages all images that its backend returns.
119
+ This includes stale outputs and outputs kept by the `"retain"` fallback. Thus,
120
+ most applications do not manage images directly.
121
+
122
+ ## React and Solid
123
+
124
+ Use the same registration pattern as other OpenTUI extension packages:
125
+
126
+ ```ts
127
+ import { registerTex } from "@simonklee/opentui-tex/react"; // or @simonklee/opentui-tex/solid
128
+
129
+ registerTex();
130
+ ```
131
+
132
+ `registerTex()` registers a `<tex>` component. Its reactive props are
133
+ `formula`, `streaming`, `display`, `foreground`, and `background`. The
134
+ component passes the other `TexRenderable` options without changes:
135
+
136
+ ```tsx
137
+ <tex
138
+ formula={String.raw`e^{i\pi} + 1 = 0`}
139
+ foreground="#ffffff"
140
+ background="#000000"
141
+ backend={backend}
142
+ />
143
+ ```
144
+
145
+ `@opentui/react` and `@opentui/solid` are optional peer dependencies. Install
146
+ the one that matches your renderer.
147
+
148
+ ## Backends
149
+
150
+ A `TexBackend` returns an owned `NativeImage` or semantic Unicode text.
151
+ The Unicode backend installs a `TextRenderable`. The native backend installs
152
+ an `ImageRenderable`.
153
+
154
+ ### Unicode cells
155
+
156
+ `UnicodeTexBackend` parses a bounded subset of TeX math into an abstract syntax
157
+ tree (AST). It lays out fractions, roots, scripts, accents, aligned equations,
158
+ matrices, and cases as two-dimensional terminal cells. The output remains
159
+ selectable terminal text. `string-width` measures the output. The backend is
160
+ stateless and synchronous, and `renderSync` is public. It needs no cache or
161
+ explicit destruction.
162
+
163
+ ### Images
164
+
165
+ Import the native backend explicitly. The package root of
166
+ `@simonklee/opentui-tex` never loads native code. The platform shared library
167
+ loads during the first native render:
168
+
169
+ ```ts
170
+ import { NativeTexBackend } from "@simonklee/opentui-tex/native";
171
+
172
+ const backend = new NativeTexBackend();
173
+ ```
174
+
175
+ On Linux musl, set `OPENTUI_LIBC=musl` before starting the application so both
176
+ OpenTUI and TeX select musl libraries. See [Native rendering](docs/native.md)
177
+ for library overrides and standalone executables.
178
+
179
+ MicroTeX parses a TeX-math dialect and computes glyph and rule positions.
180
+ ZigTeX records SVG paths for the embedded glyph outlines from Latin Modern
181
+ Math. NanoSVG rasterizes only the SVG that ZigTeX generates. It does not
182
+ rasterize arbitrary external SVG.
183
+
184
+ MicroTeX uses one process-wide font context. The native library initializes
185
+ this context once through `texInit`. It keeps the context for the process
186
+ lifetime. The renderer supports only the main JavaScript thread. If you
187
+ construct `NativeTexRenderer` in a Worker, the constructor fails immediately.
188
+
189
+ Ownership rules for direct use:
190
+
191
+ - Direct callers of `NativeTexRenderer.renderAsync()` must dispose each
192
+ returned image. Cached results use independent retained references. When you
193
+ dispose one result, the other results stay valid.
194
+ - `NativeImage.takeRaw()` requires exclusive ownership. Before you call it,
195
+ dispose all retained references. These references include the renderer cache
196
+ and renderable references.
197
+
198
+ ### Custom backends
199
+
200
+ `TexBackend` is a public interface with one method:
201
+
202
+ ```ts
203
+ interface TexBackend {
204
+ render(request: TexRenderRequest): Promise<TexRenderOutput>;
205
+ }
206
+ ```
207
+
208
+ The request contains `formula`, `display`, `foreground`, `background`,
209
+ `widthMax`, `heightMax`, and an `AbortSignal`. The output is either
210
+ `{ kind: "image", image }` or `{ kind: "unicode", text, columns, rows }`.
211
+
212
+ ## Node
213
+
214
+ Native rendering requires Node 26.4 or newer. Start Node with these options:
215
+
216
+ ```sh
217
+ node --permission --allow-fs-read=<application> --allow-ffi --experimental-ffi app.js
218
+ ```
219
+
220
+ ## Development
221
+
222
+ You can develop the package without the native toolchain.
223
+
224
+ ```sh
225
+ bun run test
226
+ bun run typecheck
227
+ bun run build:package
228
+ ```
229
+
230
+ Native builds require Zig 0.16.0 and `curl`. Use this command to test a native
231
+ build:
232
+
233
+ ```sh
234
+ bun run test:native
235
+ ```
236
+
237
+ For npm authentication, release checks, and publishing, see
238
+ [Releasing](docs/releasing.md).
@@ -0,0 +1,23 @@
1
+ import type { NativeImage } from "@opentui/core";
2
+ export interface TexRenderRequest {
3
+ formula: string;
4
+ display: boolean;
5
+ foreground: string;
6
+ background: string;
7
+ widthMax: number;
8
+ heightMax: number;
9
+ signal: AbortSignal;
10
+ }
11
+ export type TexRenderOutput = {
12
+ kind: "image";
13
+ image: NativeImage;
14
+ } | {
15
+ kind: "unicode";
16
+ text: string;
17
+ columns: number;
18
+ rows: number;
19
+ };
20
+ export interface TexBackend {
21
+ /** The receiver owns image outputs and must dispose them. */
22
+ render(request: TexRenderRequest): Promise<TexRenderOutput>;
23
+ }
@@ -0,0 +1,24 @@
1
+ import type { RenderContext } from "@opentui/core";
2
+ import { TexRenderable, type TexRenderableOptions } from "./tex-renderable.js";
3
+ export declare class BindingTexRenderable extends TexRenderable {
4
+ private bindingFormula;
5
+ private bindingStreaming;
6
+ private bindingDisplay;
7
+ private bindingForeground;
8
+ private bindingBackground;
9
+ private updateQueued;
10
+ constructor(context: RenderContext, options: TexRenderableOptions);
11
+ get formula(): string;
12
+ set formula(value: string);
13
+ get streaming(): boolean;
14
+ set streaming(value: boolean);
15
+ get display(): boolean;
16
+ set display(value: boolean);
17
+ get foreground(): string;
18
+ set foreground(value: string);
19
+ get background(): string;
20
+ set background(value: string);
21
+ setColors(foreground: string, background: string): void;
22
+ private queueUpdate;
23
+ private flushUpdate;
24
+ }
@@ -0,0 +1,4 @@
1
+ export type { TexBackend, TexRenderOutput, TexRenderRequest, } from "./backend.js";
2
+ export { measureTex, TexRenderable } from "./tex-renderable.js";
3
+ export type { TexDimensions, TexFallback, TexRenderableOptions } from "./tex-renderable.js";
4
+ export { UnicodeTexBackend } from "./unicode-tex-backend.js";