Skip to content

Latest commit

 

History

25 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TermShape

Unicode layout semantics for the terminal grid.

TermShape keeps the terminal concepts that applications need—cell ownership, grapheme-safe cursors, wrapping, truncation, selections, and direction—separate from optional font-aware glyph geometry. It is designed for CLI programs, TUI frameworks, and terminal renderers without being tied to a particular terminal, script, or renderer.

What it provides

  • A Rust semantic layout core with Unicode 17 grapheme segmentation.
  • An independent, pure-Go semantic implementation using the same shared conformance vectors.
  • Optional HarfBuzz shaping with bidi/script runs, font fallback, continuous geometry, visual cursors, and hit testing.
  • An opt-in cumulative cell allocator for consumers with real measured glyph advances that need a terminal-cell projection.
  • A stable C ABI for semantic and generic shaped native layout.
  • A reusable Ratatui semantic adapter and a small Lip Gloss fitting adapter.
  • A Ratatui visual example that presents HarfBuzz-rasterized glyph tiles in terminals with the Kitty graphics protocol.

A necessary distinction

Semantic layout and visual rendering are different jobs.

TermShape can tell an application where a cursor may stop, how text owns terminal cells, and how shaped glyph geometry relates to that text. It cannot make an unmodified terminal emulator or TUI renderer start shaping a script it does not support. A font-aware renderer must consume the shaped output itself.

Quick start

Rust semantic layout:

let layout = termshape::Layout::new("கென்னை விடு");

assert_eq!(layout.width(), 10);
assert_eq!(layout.cursor_stops(), &[0, 6, 12, 18, 19, 25, 31]);
let wrapped = layout.wrap(4)?;

Go semantic layout:

layout := termshape.NewLayout("ക്ഷേത്രം")
fmt.Println(layout.Width())
fmt.Println(layout.CursorStops())

Font-aware layout uses the optional HarfBuzz adapter. The caller chooses its fonts and its physical terminal-cell advance; TermShape does not ship or select fonts.

use termshape::{Layout, TerminalProfile};
use termshape_harfbuzz::HarfBuzzShaper;

let shaper = HarfBuzzShaper::from_file("/path/to/font.ttf")?;
let input = shaper.shape_input("கென்னை விடு", 1000.0)?;
let semantic = Layout::with_profile("கென்னை விடு", TerminalProfile::default());
let shaped = semantic.apply_shaping(&input)?;

let cursors = semantic.visual_cursors_for_byte(&shaped, 0);
let hit = semantic.hit_test_visual(&shaped, 0, 0.5);

For multiple fonts, create a HarfBuzzFontChain from loaded HarfBuzzShapers. It selects the first font that covers each resolved bidi/script run and reports a missing glyph instead of silently accepting a .notdef box.

Examples

cargo run -p termshape-ratatui-example -- --termshape
go run ./examples/bubbletea --termshape

--termshape is semantic integration: it prevents layout operations from splitting graphemes, but it still leaves glyph drawing to the framework and terminal. For actual shaped Indic glyphs in Ghostty, Kitty, or WezTerm, use the visual example with fonts that cover the selected scripts:

cargo run -p termshape-ratatui-example -- \
  --visual \
  --font /path/to/NotoSansTamil.ttf

Add one --font PATH for each needed fallback font. The visual mode uses terminal cell pixel metrics reported by the terminal; use --cell-pixels WIDTHxHEIGHT only when that metric is unavailable. It reports missing font coverage instead of displaying a .notdef glyph.

Integration status

The Rust/Ratatui visual presenter is available for Kitty-graphics terminals. The Go/Bubble Tea integration is currently semantic-only: its fitting and grapheme-safe editing are usable, but its font-aware visual renderer is still work in progress. It does not yet provide the same shaping guarantee as Rust visual mode.

The shaped reference example creates a local HTML/SVG report. It renders real text from the supplied local font files and overlays TermShape’s continuous cluster geometry and terminal-cell ownership:

cargo run -p termshape-shaped-renderer-example -- \
  --font /path/to/NotoSansTamil.ttf \
  --cell-width 1000 \
  --text 'கென்னை விடு'

It writes target/termshape-shaped.html by default. Open that local file in a browser that permits access to local font files.

Verification

cargo test --workspace --locked
cargo clippy --workspace --all-targets --locked -- -D warnings
go test -buildvcs=false ./go/termshape ./go/lipgloss ./examples/bubbletea
cc -std=c11 -fsyntax-only -x c -include include/termshape.h /dev/null

The Rust and Go implementations both consume tests/conformance/semantic-v1.json. The core also has property tests for arbitrary Unicode and arbitrary shaped input.

Repository guide

See docs for architecture, limitations, integrations, and conformance details.

About

Unicode layout semantics and font-aware Indic rendering for terminal applications.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages