omp packages

@micahdb/tscon0.2.1

WebGL 2.0 text-mode graphics in TypeScript.

micahdb Published 3 hours ago MIT ~189 downloads/mo

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.

Did it work for you?

Result

One report per person. A new report replaces your old one.

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