◉ Pulse
gakonst avatar
#56 today ▼ 12

gakonst/ratatex

Rust Other

TeX-quality display math for Ratatui with Kitty Unicode placeholders

Created Jul 29, 2026
Stars
7
Today
+1
Forks
1
Quality
90/100
Momentum
18

README

Fetched from GitHub on Oct 30, 2026, 10:17 PM UTC · README.md

View original on GitHub

Third-party content — Open Source Pulse is not the owner of this repository.

This README belongs to gakonst/ratatex and its contributors. We fetch it directly from the project’s public GitHub repository and display it for reference only. All text, code, images and trademarks remain the property of their respective authors. See the repository for its license terms. Copyright / takedown policy.

Ratatex

TeX-quality display math that scrolls with your Ratatui UI.

CI Crates.io Docs.rs License

Nanocodex demo · Try it · Embed it · Streaming · Selection · Scrolling

nanocodex-latex-stress.mp4

A live Nanocodex low-reasoning run: render TeX, click back to source, and drag-copy the original LaTeX.

Ratatex turns Markdown display math into cell-aligned PNGs on bounded background workers:

Markdown display math
    → RaTeX parse + TeX layout in-process
    → embedded KaTeX fonts + tiny-skia at 3× resolution
    → antialiasing, padding, and cell alignment
    → content-addressed PNG cache
    → Kitty virtual placement + Unicode-placeholder cells

Rendering uses the open-source RaTeX parser, layout engine, embedded fonts, and rasterizer entirely in-process. Ratatex never invokes a system TeX installation, writes DVI, or starts a rendering subprocess. Unsupported expressions return a typed failure so the host application can keep its text representation.

The placeholder cells live in the normal Ratatui buffer. Equations therefore scroll and clip with the surrounding text instead of behaving like cursor-positioned overlays.

Install

cargo add ratatex

Requirements

  • A terminal implementing Kitty graphics Unicode placeholders.
  • Ratatui 0.29.
  • No TeX distribution or external rendering programs.

Ghostty, Kitty, WezTerm, and recent Konsole releases implement the graphics protocol. When running inside tmux, enable graphics passthrough:

set -g allow-passthrough on

Try it

From a compatible terminal:

cargo run --example demo

Press q, Esc, or Ctrl-C to leave the demo.

Embed it

Query the terminal after entering the alternate screen and before starting a competing input reader. Build one renderer for the application, not one per formula:

use std::{io::Write, time::Duration};

use ratatex::{FormulaState, FormulaWidget, Ratatex, TerminalProfile};
use ratatui::{Frame, layout::Rect};

let profile = TerminalProfile::query(Duration::from_millis(750));
let renderer = Ratatex::builder(profile)
    .on_update(|| {
        // Wake your application's event loop.
    })
    .build()?;

fn draw_formula(
    frame: &mut Frame<'_>,
    area: Rect,
    renderer: &Ratatex,
    tex: &str,
) {
    if let FormulaState::Ready(formula) = renderer.request(tex, area.width) {
        frame.render_widget(FormulaWidget::new(&formula), area);
    } else {
        // Keep a text fallback or a previously ready streaming frame.
    }
}

// Before the next Ratatui draw:
for command in renderer.drain_terminal_commands() {
    std::io::stdout().write_all(command.as_bytes())?;
}

# Ok::<(), std::io::Error>(())

request() is non-blocking. A cache miss returns Pending, renders in-process on a worker, and invokes on_update when the result is ready. Subsequent runs can load the content-addressed PNG directly from disk.

When the callback wakes your event loop:

  1. Invalidate cached layout if generation() changed.
  2. Drain terminal commands and write them before drawing their placeholders.
  3. Draw the next Ratatui frame.

See examples/demo.rs for a complete crossterm loop.

Streaming Markdown

You do not need to wait for the model to close \], $$, aligned, braces, or \left before beginning a render. heal_streaming_display_math() returns a temporary view with only the missing closing tokens appended:

use ratatex::{
    Formula, FormulaState, Ratatex, display_math, heal_streaming_display_math,
};
use std::sync::Arc;

fn update_streaming_formula(
    renderer: &Ratatex,
    markdown_prefix: &str,
    width: u16,
    last_ready: &mut Option<Arc<Formula>>,
) {
    let preview = heal_streaming_display_math(markdown_prefix);
    let Some(region) = display_math(&preview).last() else {
        return;
    };

    if let FormulaState::Ready(formula) = renderer.request(region.source(), width) {
        *last_ready = Some(formula);
    }
}

The helper never alters existing stream bytes. Keep the real Markdown for copying, and keep displaying last_ready while a newer prefix is Pending or temporarily unparsable. This makes a formula refine in place instead of showing raw TeX and flashing into an image only at the end.

For fast streams, retain a short history of submitted prefixes. A slightly older request may finish after a newer prefix has already arrived; accepting that completed frame gives the user useful progressive output while the newest request catches up.

display_math() and markdown_segments() recognize $$…$$, \[…\], and common AMS display environments while ignoring fenced and inline code. Ratatex intentionally leaves prose, inline math, and Markdown styling to the host application.

Copyable LaTeX

For native terminal selection and copying, render the same widget with its original display-math region during your application's selection pass:

let clicked_formula =
    ratatex::is_formula_placeholder(frame.buffer()[mouse_position].symbol());
let widget = FormulaWidget::new(&formula);
let widget = if clicked_formula || selection_intersects_formula {
    widget.compact_source_fallback(display_math.full_source())
} else {
    widget
};
frame.render_widget(widget, area);

Keep the fallback enabled after the first click while the user starts their selection. The compact fallback keeps the formula's existing rows and folds source newlines into spaces, so enabling it does not reflow the surrounding transcript or clip a closing delimiter in common display blocks. Your normal selection highlight can then operate on ordinary LaTeX cells instead of Kitty placeholder characters. Use source_fallback when exact source whitespace is more important and the application reserves enough rows for it.

Scrolling and clipping

Reserve exactly Formula::rows() transcript rows. When part of the formula is above the viewport, keep the formula's logical origin and pass its signed offset to the widget:

use ratatex::{FormulaWidget, SignedPosition};

FormulaWidget::new(&formula)
    .position(SignedPosition::new(0, -rows_scrolled_off_top))
    .render(visible_area, frame.buffer_mut());

Do not slice the placeholder grid and redraw it from tile (0, 0). Kitty Unicode placeholders encode each tile's source row and column; preserving those coordinates is what keeps partially visible equations intact while scrolling up and down.

Call reupload_all() after recreating the alternate screen and when a tmux pane regains focus. tmux retains the placeholder cells for hidden panes but normally drops graphics passthrough sent while the pane is not visible.

Rendering choices

The default is a transparent grayscale alpha mask with a neutral off-white foreground. It works across terminal themes without guessing the background. ColorScheme and Antialiasing::{LcdRgb,LcdBgr} provide LCD subpixel rendering when the application knows the exact background color.

TeX input, decoded images, queues, and caches are bounded, and unsupported or unsafe primitives are rejected before layout. Formula input is parsed as math; it is never passed to a shell or external TeX process. Run untrusted rendering in an OS sandbox when it requires a hard security boundary.

Scope

Ratatex renders display math. It does not own your Markdown parser, transcript model, scrolling state, input loop, or clipboard. The API is deliberately a small composable layer: detection and streaming healing, an asynchronous renderer/cache, terminal commands, and a clipping Ratatui widget.

The supported formula language is the KaTeX/AMS-style subset implemented by RaTeX. Ratatex intentionally does not load arbitrary TeX packages or fall back to a system TeX toolchain.

License

MIT

Rendered by GitHub from the repository’s README. Scroll to read the full file.
First trending

Jul 30, 2026

first spotted on the list

Best rank

#47

highest position reached

Appearances

2

times on the trending list

Quality score

90

out of 100

Momentum

18

trend strength indicator

Star velocity

0.08

stars per day since creation

Activity — last 60 days

daily counts, collected every 6 hours
  • Stars
  • Forks
  • Merged PRs
  • Issues
  • Closed issues

Collecting activity data since launch — check back in a few days.

Star history

total stars per snapshot
5678gakonst/ratatex · Jul 30, 2 AM UTC: 6 starsgakonst/ratatex · Jul 30, 8 AM UTC: 7 stars2:39 AM5:36 AM8:32 AM

Rank history

position on trending · lower is better
#1#46#48#50#52#54#56Jul 30, 2 AM UTC: rank #47Jul 30, 8 AM UTC: rank #562:39 AM5:36 AM8:32 AM

3D card for your website

Live HTML card with a glass surface and pointer tilt. Paste the iframe on any website.

<iframe src="https://www.notionpulse.cc/embed/gakonst/ratatex" width="420" height="240" style="border:0;max-width:100%;background:transparent" loading="lazy" title="gakonst/ratatex — trending on Open Source Pulse"></iframe>

Stars badge

Show current stars in your README or profile. Auto-updates every 6 hours.

gakonst/ratatex stars badge
Markdown
[![gakonst/ratatex](https://www.notionpulse.cc/api/badge/gakonst/ratatex.svg)](https://www.notionpulse.cc/repo/gakonst/ratatex)
HTML
<a href="https://www.notionpulse.cc/repo/gakonst/ratatex"><img src="https://www.notionpulse.cc/api/badge/gakonst/ratatex.svg" alt="gakonst/ratatex stars badge" /></a>

Repository of the Day

#47 Repository of the Day

Repository of the Week

#47 Repository of the Week

Repository of the Month

#47 Repository of the Month