@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.
@@ -0,0 +1,67 @@
1
+ import { BoxRenderable, type BoxOptions, type ImageRenderableOptions, type RenderContext } from "@opentui/core";
2
+ import type { TexBackend } from "./backend.js";
3
+ export type { TexBackend, TexRenderOutput, TexRenderRequest } from "./backend.js";
4
+ export type TexFallback = "message" | "throw" | "retain" | "unicode";
5
+ export interface TexRenderableOptions extends BoxOptions {
6
+ formula: string;
7
+ display?: boolean;
8
+ foreground: string;
9
+ background: string;
10
+ widthMax?: number;
11
+ heightMax?: number;
12
+ backend: TexBackend;
13
+ fallback?: TexFallback;
14
+ imageOptions?: Omit<ImageRenderableOptions, "source" | "width" | "height">;
15
+ onError?: (error: unknown) => void;
16
+ streaming?: boolean;
17
+ }
18
+ export interface TexDimensions {
19
+ columns: number;
20
+ rows: number;
21
+ }
22
+ export declare function measureTex(width: number, height: number, display: boolean, widthMax: number, heightMax: number): TexDimensions;
23
+ export declare function fitImageToPlacement(imageWidth: number, imageHeight: number, columns: number, rows: number, cellPxWidth: number, cellPxHeight: number): {
24
+ width: number;
25
+ height: number;
26
+ } | null;
27
+ export declare class TexRenderable extends BoxRenderable {
28
+ ready: Promise<void>;
29
+ private readonly backend;
30
+ private readonly fallback;
31
+ private readonly widthMax;
32
+ private readonly heightMax;
33
+ private autoWidth;
34
+ private autoHeight;
35
+ private trackExplicitDimensions;
36
+ private currentDimensions;
37
+ private readonly imageOptions?;
38
+ private readonly onError?;
39
+ private _formula;
40
+ private _foreground;
41
+ private _background;
42
+ private _display;
43
+ private _streaming;
44
+ private controller;
45
+ private committedOutput;
46
+ constructor(context: RenderContext, options: TexRenderableOptions);
47
+ get formula(): string;
48
+ set formula(value: string);
49
+ get display(): boolean;
50
+ set display(value: boolean);
51
+ get width(): number;
52
+ set width(value: number | "auto" | `${number}%`);
53
+ get height(): number;
54
+ set height(value: number | "auto" | `${number}%`);
55
+ get streaming(): boolean;
56
+ set streaming(value: boolean);
57
+ setColors(foreground: string, background: string): void;
58
+ protected setSnapshot(formula: string, foreground: string, background: string, display?: boolean): void;
59
+ whenReady(): Promise<void>;
60
+ private update;
61
+ private previewOutput;
62
+ private clearOutput;
63
+ private applyOutput;
64
+ private createImageChild;
65
+ private resizeToPlacement;
66
+ protected destroySelf(): void;
67
+ }
@@ -0,0 +1,11 @@
1
+ import type { TexBackend, TexRenderOutput, TexRenderRequest } from "./backend.js";
2
+ export declare const UNICODE_TEX_SOURCE_LENGTH_MAX = 4096;
3
+ type UnicodeTexRenderOutput = Extract<TexRenderOutput, {
4
+ kind: "unicode";
5
+ }>;
6
+ export declare class UnicodeTexBackend implements TexBackend {
7
+ renderSync(request: TexRenderRequest): UnicodeTexRenderOutput;
8
+ render(request: TexRenderRequest): Promise<TexRenderOutput>;
9
+ }
10
+ export declare function renderIncompleteUnicode(request: TexRenderRequest): UnicodeTexRenderOutput;
11
+ export {};
package/docs/native.md ADDED
@@ -0,0 +1,32 @@
1
+ # Native rendering
2
+
3
+ The native TeX backend is part of `@simonklee/opentui-tex`.
4
+
5
+ ```ts
6
+ import { NativeTexBackend } from "@simonklee/opentui-tex/native"
7
+ ```
8
+
9
+ `NativeTexRenderer.renderAsync()` returns an owned OpenTUI `NativeImage`. The
10
+ caller must dispose every returned image. Cached images use independent retained
11
+ references, so callers can dispose results without invalidating the cache or
12
+ other results. `TexRenderable` manages images returned through `NativeTexBackend`.
13
+
14
+ The native renderer returns borrowed RGBA pixels to JavaScript, which copies
15
+ them synchronously into OpenTUI native-image ownership. The rendering path is
16
+ free of image encoding and decoding, but it is not zero-copy because the TeX and
17
+ OpenTUI native libraries are separate.
18
+
19
+ `NativeImage.takeRaw()` requires exclusive ownership. Destroy the renderer and
20
+ dispose all other retained references before transferring its pixels.
21
+
22
+ The package loads a prebuilt library for the current operating system,
23
+ architecture, and Linux libc. Set `OPENTUI_LIBC=glibc` or `OPENTUI_LIBC=musl`
24
+ to override Linux selection.
25
+
26
+ Standalone executables can extract the selected shared library and set
27
+ `OPENTUI_LATEX_NATIVE_PATH` to its absolute path.
28
+
29
+ The native context is process-owned. `destroy()` releases renderer-instance
30
+ queues and caches. Node 26.4 can retain its experimental FFI handle during
31
+ shutdown, so short-lived Node programs may need to call `process.exit()` after
32
+ application cleanup. Re-test this restriction when upgrading Node's FFI.
@@ -0,0 +1,130 @@
1
+ # Releasing
2
+
3
+ Users install `@simonklee/opentui-tex`. Each release publishes ten npm packages:
4
+ the JavaScript package, eight optional native packages, and the corresponding
5
+ native source package. All ten have the same version.
6
+
7
+ The native packages use the same target matrix and Bun/Node library-path exports
8
+ as OpenTUI. npm and Bun select a binary through `os`, `cpu`, and Linux `libc`
9
+ constraints. The JavaScript package keeps `@opentui/core` external and declares
10
+ it as a required peer. React and Solid are optional peers.
11
+
12
+ ## First publication
13
+
14
+ npm requires a package to exist before you can configure its trusted publisher.
15
+ Publish all ten packages locally with an interactive npm login and two-factor
16
+ authentication first. Do not create an `NPM_TOKEN` secret.
17
+
18
+ 1. Commit and push the release changes to `main`.
19
+ 2. Create and push the matching `v0.2.0` tag. Do not publish the GitHub release yet.
20
+ 3. Run `Release` manually with `tag` set to `v0.2.0` and `dry-run` enabled.
21
+ 4. After the run succeeds, download its `npm-packages` artifact into a clean
22
+ `release/` directory. Keep only the ten tarballs from that run.
23
+ 5. From the repository root at the tagged commit, run:
24
+
25
+ ```sh
26
+ npm login
27
+ RELEASE_TAG=v0.2.0 bun run publish:packages
28
+ ```
29
+
30
+ Complete npm's browser or terminal authentication prompts. The publisher uses
31
+ your local npm login and publishes the tested tarballs in dependency order. If
32
+ the process stops partway through, rerun the same command without rebuilding.
33
+ The publisher checks npm before skipping an already-published version.
34
+
35
+ You can instead build and test the tarballs locally with `bun run release:check`
36
+ at the tagged commit before running these publication commands.
37
+
38
+ After the packages exist, configure a trusted publisher for **each of the ten
39
+ packages**. With npm 11.19.0, the command for the main package is:
40
+
41
+ ```sh
42
+ npm trust github @simonklee/opentui-tex \
43
+ --repo simonklee/opentui-tex \
44
+ --file release.yml \
45
+ --env npm \
46
+ --allow-publish
47
+ ```
48
+
49
+ Repeat it for `@simonklee/opentui-tex-native-source` and each of the eight names
50
+ in `package.json`'s `optionalDependencies`, with the same options. The npm
51
+ website offers the same fields under **Settings > Trusted Publisher**:
52
+
53
+ | Field | Value |
54
+ | --- | --- |
55
+ | Provider | GitHub Actions |
56
+ | Organization or user | `simonklee` |
57
+ | Repository | `opentui-tex` |
58
+ | Workflow filename | `release.yml` |
59
+ | Environment | `npm` |
60
+ | Allowed action | `npm publish` |
61
+
62
+ Create the GitHub environment `npm` in `simonklee/opentui-tex` and restrict it to
63
+ tags named `v*`. You can also require approval before publication. Only the
64
+ publish job uses this environment and receives `id-token: write`; build and
65
+ dry-run jobs need neither. No npm token secret is needed.
66
+
67
+ Later releases use OIDC authentication and receive npm provenance automatically
68
+ from this public repository. After a successful trusted release, select
69
+ **Require two-factor authentication and disallow tokens** in each package's
70
+ publishing settings. Local interactive publication remains available.
71
+
72
+ See [npm's trusted publishing guide](https://docs.npmjs.com/trusted-publishers/)
73
+ and [`npm trust`](https://docs.npmjs.com/cli/v11/commands/npm-trust) for the
74
+ account-side settings. Repository files alone cannot grant npm access.
75
+
76
+ ## Validate a release
77
+
78
+ Use Bun 1.3.14, Zig 0.16.0, Node 26.4 or newer, npm 11.19.0 or newer, `curl`, and
79
+ `tar`. Run:
80
+
81
+ ```sh
82
+ bun install --frozen-lockfile
83
+ bun run release:check
84
+ ```
85
+
86
+ The check runs type checking, JavaScript and Zig tests, all native builds, and
87
+ package builds. It writes ten tarballs to `release/`. A local scoped registry
88
+ serves those tarballs to clean npm and Bun installs. The smoke tests verify
89
+ platform selection, Unicode output, native rendering, and shared OpenTUI class
90
+ identity on both Bun and Node. They also check that imports still work without
91
+ the TeX binary. The publisher then validates the tarballs and prints the publish
92
+ order without uploading packages.
93
+
94
+ The release runner tests native execution on Linux x64. Cross-compilation does
95
+ not establish runtime coverage on the other seven targets.
96
+
97
+ ## Publish
98
+
99
+ Version 0.2.0 is the first single-install release. It replaces the separate
100
+ native JavaScript package from the 0.1.0 GitHub release. Keep the existing 0.1.0
101
+ tags and tarballs unchanged.
102
+
103
+ For later versions, update the root version and all native dependency versions
104
+ together:
105
+
106
+ ```sh
107
+ npm version 0.2.1 --no-git-tag-version
108
+ ```
109
+
110
+ The `version` script also updates `bun.lock`. Commit the version change and the
111
+ release code, then create and push the matching `v<version>` tag. Publish a
112
+ GitHub release for that tag to run the workflow. The tag must match
113
+ `package.json` exactly.
114
+
115
+ You can also run `Release` manually. Leave `dry-run` enabled to validate a branch
116
+ or tag without publishing. To publish manually, supply the existing release tag
117
+ and disable `dry-run`. Select that tag as the workflow ref too, so the run can
118
+ deploy to the tag-restricted `npm` environment.
119
+
120
+ The workflow publishes the tested tarballs, not rebuilt copies. It publishes
121
+ corresponding source first, native packages next, and the JavaScript package
122
+ last. Stable versions use the npm `latest` tag; prereleases use `next`.
123
+ Publication is not atomic. If publication stops partway through, rerun the failed
124
+ publish job to reuse the tarballs from the successful build job. The publisher
125
+ checks npm before skipping an already-published version.
126
+
127
+ Do not use plain `npm publish` from the repository root for a release: that
128
+ would omit the native packages. The coordinated command is
129
+ `RELEASE_TAG=v<version> bun run publish:packages`, after
130
+ `bun run release:check` succeeds. The named tag must point to `HEAD`.
package/package.json ADDED
@@ -0,0 +1,103 @@
1
+ {
2
+ "name": "@simonklee/opentui-tex",
3
+ "version": "0.2.0",
4
+ "description": "TeX renderable with Unicode and native image backends for OpenTUI",
5
+ "license": "MIT",
6
+ "packageManager": "bun@1.3.14",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/simonklee/opentui-tex.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/simonklee/opentui-tex/issues"
13
+ },
14
+ "homepage": "https://github.com/simonklee/opentui-tex#readme",
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "module": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "files": [
20
+ "dist",
21
+ "docs",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
+ },
30
+ "./native": {
31
+ "types": "./dist/native.d.ts",
32
+ "bun": "./dist/native.bun.js",
33
+ "import": "./dist/native.js"
34
+ },
35
+ "./react": {
36
+ "types": "./dist/react.d.ts",
37
+ "import": "./dist/react.js"
38
+ },
39
+ "./solid": {
40
+ "types": "./dist/solid.d.ts",
41
+ "import": "./dist/solid.js"
42
+ }
43
+ },
44
+ "scripts": {
45
+ "build": "bun run build:native && bun run build:package",
46
+ "build:package": "bun scripts/build-package.ts",
47
+ "build:demo": "bun build demo/app.ts --outdir dist --target bun --packages external",
48
+ "build:demo:node": "bun build demo/app.ts --outfile dist/app.node.js --target node --packages external",
49
+ "build:native": "bash scripts/build-native",
50
+ "build:native:all": "bash scripts/build-native --all",
51
+ "build:release": "bun run build:package && bun run build:native:all && bun scripts/package-release.ts",
52
+ "dev": "bun demo/app.ts",
53
+ "start": "bun demo/app.ts",
54
+ "start:node": "bun run build:demo:node && node --permission --allow-fs-read=.. --allow-child-process --allow-worker --allow-ffi --experimental-ffi dist/app.node.js",
55
+ "test": "bun test src/document.test.ts src/tex-renderable.test.ts src/unicode-tex-backend.test.ts",
56
+ "test:native": "bun run build:native && bun run test:native:zig && bun test src/native",
57
+ "test:native:zig": "bash scripts/bootstrap-native && zig build test",
58
+ "test:release": "bun scripts/test-release.ts",
59
+ "test:all": "bun run test && bun run test:native",
60
+ "release:check": "bun run typecheck && bun run test:all && bun run build:release && bun run test:release && bun scripts/publish-packages.ts --dry-run",
61
+ "publish:packages": "bun scripts/publish-packages.ts",
62
+ "typecheck": "tsc --noEmit",
63
+ "version": "bun scripts/version.ts && bun install --lockfile-only"
64
+ },
65
+ "dependencies": {
66
+ "string-width": "7.2.0"
67
+ },
68
+ "peerDependencies": {
69
+ "@opentui/core": "0.0.0-20260812-1d34234c",
70
+ "@opentui/react": "0.0.0-20260812-1d34234c",
71
+ "@opentui/solid": "0.0.0-20260812-1d34234c"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "@opentui/react": { "optional": true },
75
+ "@opentui/solid": { "optional": true }
76
+ },
77
+ "devDependencies": {
78
+ "@opentui/core": "0.0.0-20260812-1d34234c",
79
+ "@opentui/react": "0.0.0-20260812-1d34234c",
80
+ "@opentui/solid": "0.0.0-20260812-1d34234c",
81
+ "@types/bun": "latest",
82
+ "@types/node": "^24.0.0",
83
+ "typescript": "^5"
84
+ },
85
+ "optionalDependencies": {
86
+ "@simonklee/opentui-tex-native-darwin-arm64": "0.2.0",
87
+ "@simonklee/opentui-tex-native-darwin-x64": "0.2.0",
88
+ "@simonklee/opentui-tex-native-linux-arm64": "0.2.0",
89
+ "@simonklee/opentui-tex-native-linux-arm64-musl": "0.2.0",
90
+ "@simonklee/opentui-tex-native-linux-x64": "0.2.0",
91
+ "@simonklee/opentui-tex-native-linux-x64-musl": "0.2.0",
92
+ "@simonklee/opentui-tex-native-win32-arm64": "0.2.0",
93
+ "@simonklee/opentui-tex-native-win32-x64": "0.2.0"
94
+ },
95
+ "publishConfig": {
96
+ "access": "public",
97
+ "registry": "https://registry.npmjs.org/"
98
+ },
99
+ "engines": {
100
+ "bun": ">=1.3.0",
101
+ "node": ">=26.4.0"
102
+ }
103
+ }