npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@relictombs/opentui-math

v0.2.0

Published

Beautiful LaTeX math rendering for OpenTUI

Readme

@relictombs/opentui-math

npm license OpenTUI

Beautiful LaTeX math rendering for OpenTUI.

@relictombs/opentui-math includes two complementary renderers:

  • A universal Unicode cell renderer that works in every terminal supported by OpenTUI.
  • A high-resolution MathJax renderer for Kitty-compatible terminal graphics, with automatic cell fallback.

It supports intrinsic Yoga layout, live updates, React and Solid elements, standalone string/SVG/PNG rendering, and partial LaTeX arriving from an AI or network stream.

           ╭────────
     -b ± √ b² - 4ac
x = ─────────────────
           2a

Installation

Install the package with OpenTUI:

npm install @relictombs/opentui-math @opentui/core
bun add @relictombs/opentui-math @opentui/core
pnpm add @relictombs/opentui-math @opentui/core

Requires OpenTUI 0.4.5 or newer. React and Solid integrations use the matching optional @opentui/react or @opentui/solid peer dependency.

Quick start

import { createCliRenderer } from "@opentui/core"
import { LatexRenderable } from "@relictombs/opentui-math"

const renderer = await createCliRenderer()
const formula = new LatexRenderable(renderer, {
  content: String.raw`x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}`,
  foregroundColor: "#cdd6f4",
  displayMode: true,
})

renderer.root.add(formula)

The renderable measures itself through Yoga. Changing formula.content reparses, remeasures, and repaints the same component:

formula.content = String.raw`\lim_{n\to\infty}\left(1+\frac{1}{n}\right)^n=e`

High-resolution graphics

Use GraphicalLatexRenderable for antialiased New Computer Modern glyphs and browser-quality math spacing:

import { createCliRenderer } from "@opentui/core"
import { GraphicalLatexRenderable } from "@relictombs/opentui-math/graphics"

const renderer = await createCliRenderer()
const formula = new GraphicalLatexRenderable(renderer, {
  content: String.raw`\int_0^\infty e^{-x^2}\,dx = \frac{\sqrt{\pi}}{2}`,
  foregroundColor: "#f4f4f5",
  fontSize: 36,
})

renderer.root.add(formula)
await formula.whenGraphicsReady()

The default graphicsMode: "auto" uses Kitty graphics when supported and falls back to Unicode cells everywhere else. Use "kitty" to force graphics or "cells" to force the universal renderer.

High-resolution rendering keeps a bounded set of owned raw RGBA snapshots in memory and delegates direct Kitty transmission, placement, replacement, and cleanup to the shared Relictombs terminal packages. No temporary RGBA files or host-local pathnames are involved, and @relictombs/opentui-math does not maintain a second Kitty protocol implementation.

Known compatible graphics terminals include:

| Terminal | High-resolution mode | | --------------------------- | ----------------------- | | Ghostty | Yes | | Kitty | Yes | | WezTerm | Yes | | Other terminals | Automatic cell fallback | | tmux, Zellij, or GNU Screen | Cell fallback |

Kitty images retain a stable cell footprint. Zooming the terminal font therefore enlarges the formula along with the rest of the interface. pixelRatio increases raster sharpness without changing that logical size.

Effect ownership

Use @relictombs/opentui-math/effect for application orchestration. MathJax and Resvg failures become schema-backed MathGraphics.OperationError values, graphical renderables are destroyed with their scope, and stream timers cannot outlive their owner:

import { Effect } from "effect"
import { LatexStream, MathGraphics } from "@relictombs/opentui-math/effect"

const program = Effect.gen(function* () {
  const graphics = yield* MathGraphics.Service
  const formula = yield* graphics.open(renderer, {
    content: "",
    fallback: "source",
  })

  const stream = yield* LatexStream.open(formula.renderable, { updateIntervalMs: 25 })
  yield* stream.append(String.raw`\frac{1}{2}`)
  const result = yield* stream.finish()
  yield* Effect.logInfo("formula settled", result)
}).pipe(Effect.provide(MathGraphics.layer), Effect.scoped)

await Effect.runPromise(program)

MathGraphics.renderSvg() and MathGraphics.renderPng() provide named Effect operations for standalone output. The Promise APIs in @relictombs/opentui-math/graphics remain adapters for non-Effect hosts.

SVG and PNG output

The graphics entry point can render without a TUI:

import { writeFile } from "node:fs/promises"
import { renderLatexToPng, renderLatexToSvg } from "@relictombs/opentui-math/graphics"

const svg = await renderLatexToSvg(String.raw`E = mc^2`)
const image = await renderLatexToPng(String.raw`\sqrt{x^2+y^2}`, {
  fontSize: 48,
  pixelRatio: 2,
})

await writeFile("formula.png", image.png)

Streaming partial LaTeX

AI responses and network streams often contain temporarily invalid prefixes. Assigning each token directly can cause parse-error flicker and unnecessary image renders.

LatexStreamController coalesces deltas and can temporarily close open arguments, \left delimiters, and environments without altering the accumulated source:

import { completeLatexPrefix, GraphicalLatexRenderable, LatexStreamController } from "@relictombs/opentui-math/graphics"

const formula = new GraphicalLatexRenderable(renderer, {
  content: "",
  fallback: "source",
  strict: true,
  foregroundColor: "#a6e3a1",
})
renderer.root.add(formula)

const stream = new LatexStreamController(formula, {
  incompletePolicy: "apply",
  preview: completeLatexPrefix,
  updateIntervalMs: 25,
  validationOptions: { strict: true },
})

for await (const latexDelta of latexDeltas) {
  stream.append(latexDelta)
}

const result = await stream.finish()
if (!result.applied) console.error(result.error)

An unrepairable fragment such as \beg is shown as source. A repairable prefix such as \frac{1}{ is rendered using a temporary \frac{1}{} preview. Once the stream is complete, the exact received LaTeX replaces the preview.

The default incompletePolicy: "retain" keeps the previous valid formula instead of displaying raw invalid source.

React

Register the intrinsic element once before rendering:

import { createRoot } from "@opentui/react"
import { registerLatex } from "@relictombs/opentui-math/react"

registerLatex()

createRoot(renderer).render(
  <latex content={String.raw`\sum_{i=1}^{n} i^2 = \frac{n(n+1)(2n+1)}{6}`} foregroundColor="#89b4fa" />,
)

For high-resolution React output, import registerGraphicalLatex from @relictombs/opentui-math/graphics/react and render <latexImage>.

Solid

import { render } from "@opentui/solid"
import { registerLatex } from "@relictombs/opentui-math/solid"

registerLatex()

render(() => <latex content={String.raw`\int_0^\infty e^{-x}\,dx = 1`} />, renderer)

The high-resolution Solid entry point is @relictombs/opentui-math/graphics/solid.

Pure cell renderer

The parser and layout engine also work without a running TUI:

import { renderLatexToString } from "@relictombs/opentui-math"

console.log(renderLatexToString(String.raw`\begin{pmatrix}a & b \\ c & d\end{pmatrix}`))
⎛a b⎞
⎜   ⎟
⎝c d⎠

renderLatex() returns { width, height, baseline, cells, toString() } for testing and custom composition.

Package entry points

| Import | Purpose | | ----------------------------------------- | -------------------------------------------------------- | | @relictombs/opentui-math | Cell parser, layout, renderer, renderable, and streaming | | @relictombs/opentui-math/effect | Typed graphics operations and scoped resource ownership | | @relictombs/opentui-math/react | React <latex> registration | | @relictombs/opentui-math/solid | Solid <latex> registration | | @relictombs/opentui-math/graphics | High-resolution renderable and SVG/PNG functions | | @relictombs/opentui-math/graphics/react | React <latexImage> registration | | @relictombs/opentui-math/graphics/solid | Solid <latexImage> registration |

Options

Cell renderer

| Option | Default | Purpose | | ------------------- | ----------------- | ----------------------------------------------------- | | content | "" | LaTeX math source | | foregroundColor | #e8e8f0 | Formula color | | backgroundColor | transparent | Formula background | | displayMode | true | Put limits above and below large operators | | compactScripts | true | Use Unicode super/subscripts when exact glyphs exist | | macros | {} | Expand lightweight user command macros | | maxSourceLength | 100000 | Reject unexpectedly large formulas | | maxExpandedLength | maxSourceLength | Bound macro-expanded output | | maxDepth | 256 | Bound nested groups and commands | | strict | false | Throw on unknown commands | | fallback | "message" | Error behavior: "message", "source", or "throw" | | errorColor | #ff6b6b | Fallback error color |

Graphics renderer

| Option | Default | Purpose | | ------------------------- | ----------------- | ---------------------------------------- | | graphicsMode | "auto" | Choose "auto", "kitty", or "cells" | | fontSize | 32 | Math font size in CSS pixels | | pixelRatio | 1 | Raster output scale | | maxRasterWidth | 8192 | Maximum allocated bitmap width | | maxRasterHeight | 8192 | Maximum allocated bitmap height | | maxRasterPixels | 16777216 | Maximum bitmap area | | graphicsForegroundColor | foregroundColor | CSS color used for the image | | graphicsZIndex | 1 | Kitty placement stacking order |

Stream controller

| Option | Default | Purpose | | ------------------- | --------------- | --------------------------------------------------------------- | | updateIntervalMs | 75 | Quiet period used to coalesce token deltas | | maxBufferLength | 100000 | Maximum accumulated stream length | | incompletePolicy | "retain" | Retain the last frame or apply incomplete source | | validationOptions | {} | Parser options used for completeness checks | | validate | tolerant parser | Custom completeness check | | preview | none | Build a temporary renderable source without changing the stream |

Supported LaTeX

  • Fractions, binomials, square roots, and indexed roots
  • Superscripts, subscripts, Greek letters, relations, arrows, and binary operators
  • Integrals, sums, products, limits, derivatives, and common named operators
  • Stretching parentheses, brackets, braces, bars, floors, and ceilings
  • matrix, pmatrix, bmatrix, Bmatrix, vmatrix, Vmatrix, smallmatrix, cases, and array
  • aligned, align, and gathered, including starred alignment forms
  • Accents such as \hat, \bar, \vec, \tilde, \dot, \ddot, \overline, and \underline
  • \text, \operatorname, \overset, \underset, colors, and lightweight macros

The cell backend is a math-mode renderer rather than a complete TeX engine. It does not compile documents, load packages, execute arbitrary TeX, or render TikZ. The graphics backend accepts the TeX input supported by MathJax. Neither backend shells out to a TeX installation.

Development

From a repository checkout, install dependencies at the workspace root and run package commands from packages/opentui/math:

cd packages/opentui/math
bun run test
bun run check
bun run build

Run the demos in a compatible terminal:

bun run demo
bun run demo:graphics
bun run demo:stream

License

MIT