@micahdb/tscon0.2.1
WebGL 2.0 text-mode graphics in TypeScript.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
@micahdb/tscon
WebGL 2.0 text-mode graphics in TypeScript.
tscon draws a <canvas> as a grid of character cells. Text is written once,
compiled into that grid, and drawn with a single GPU call, so a web page can
be laid out, styled, and interacted with as plain characters. It also renders
live textures as characters, which makes photographic and 3D content sit
inside the same grid.
Install
bun add @micahdb/tscon
# or
npm install @micahdb/tscon
Usage
import { Canvas, Glyphs, Terminal } from "@micahdb/tscon";
const canvas = new Canvas(document.querySelector("canvas")!);
const terminal = new Terminal(canvas);
// compile the text once, at the width you want it to fill
const glyphs = new Glyphs("Hello, tscon!", canvas.cols);
// draw: clear the frame, blit the grid, present it
canvas.clear();
terminal.clear();
terminal.blit(
glyphs,
{ row: 0, col: 0, rows: glyphs.rows, cols: glyphs.cols },
{ row: 0, col: 0, rows: glyphs.rows, cols: glyphs.cols },
);
terminal.draw();
Everything is measured in cells, never pixels: canvas.rows by canvas.cols
cells, roughly 8x16 CSS pixels each. Sizes come from the canvas, and regions
are { row, col, rows, cols }.
Text
Text is a plain string with escape sequences for styling and for marking positions:
| Escape | Meaning |
|---|---|
\fX / \FX |
foreground, dark / bright palette index X |
\bX / \BX |
background, dark / bright palette index X |
\aX / \aX{options} |
an anchor in group X (0-9), with optional payload |
\\ |
a literal backslash |
A newline starts a new line, \ before a newline joins two lines, and a tab
advances to the next multiple of TAB_WIDTH.
Glyphs compiles text for a given width. The grid it produces can be blitted
directly, and can also answer where things ended up:
import { Glyphs, Wrap } from "@micahdb/tscon";
const g = new Glyphs("Click \\a0{here|#}here", 40);
g.renderedCell(17); // { row: 0, col: 6 }: where the "h" draws
g.renderedCell(6); // null: index 6 is inside the anchor
g.cursorCell(6); // { row: 0, col: 6 }: a cursor here shows on the "h"
g.cursorIndex(0, 9); // 20: the source character under that cell
g.spansFor(17, 21); // [{ row: 0, from: 6, to: 10 }]
g.rowCols(0); // 10: "Click here"
const hard = new Glyphs("long text", 4, { wrap: Wrap.HARD });
hard.wrappedText; // ["long", " tex", "t"]
col indices are positions in the source string, escapes included; row and
col from the position methods are cells in the compiled grid.
Anchors
An anchor marks a cell for the application without occupying space of its
own. glyphs.anchors[group] lists them, each with the payload and the cell of
the character that follows it. What a group means is up to you: links,
inline images, headings, hit targets.
const g = new Glyphs("\\a0{Docs|#docs}Docs", 20);
const link = g.anchors[0]![0]!; // { options: "Docs|#docs", row: 0, col: 0 }
Textures
Renderer draws a texture into a region as cells, either as the texture
itself or converted into coloured characters:
import { Renderer, TexGlyphMode, textureGlyphs } from "@micahdb/tscon";
const renderer = new Renderer(canvas);
const grid = textureGlyphs(24, 48, TexGlyphMode.BG_GLYPHS);
renderer.draw(grid, someWebGLTexture, { row: 0, col: 0, rows: 24, cols: 48 });
Textures composite under text: blank cells are transparent, so text stays readable over a rendered scene.
Escape hatch
Glyphs.mapCells derives an altered copy of a compiled grid -- a cursor
inversion, a selection shade, a cleared region -- without touching the
original or losing its positions:
// blank a strip, so whatever is drawn beneath it shows through
const cleared = glyphs.mapCells(
() => 0,
{ row: 2, col: 0, rows: 1, cols: 10 },
);
Development
This project uses Bun as its primary toolchain (installs,
scripts, and publishing) with the TypeScript compiler for type-checking and
dist/ emission.
bun install # install dev dependencies
bun run shaders # regenerate src/shaders.ts from shaders/
bun test # run the test suite
bun run typecheck # type-check only
bun run lint # lint
bun run build # type-check + emit dist/ (JS + .d.ts)
Source files import each other with explicit .ts extensions so they run
natively under Bun; the build rewrites those specifiers to .js in the
published output via rewriteRelativeImportExtensions.
License
MIT