termdot 0.0.1 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -21,8 +21,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  BRAILLE_OFFSET: () => BRAILLE_OFFSET,
24
+ DOT_MASKS: () => DOT_MASKS,
24
25
  PIXEL_MAP: () => PIXEL_MAP,
25
26
  animate: () => animate,
27
+ braille: () => braille,
28
+ brailleDots: () => brailleDots,
29
+ brailleGrid: () => brailleGrid,
30
+ brailleIcon: () => brailleIcon,
26
31
  clear: () => clear,
27
32
  createCanvas: () => createCanvas,
28
33
  createFixedCanvas: () => createFixedCanvas,
@@ -61,6 +66,24 @@ var PIXEL_MAP = [
61
66
  [4, 32],
62
67
  [64, 128]
63
68
  ];
69
+ var DOT_MASKS = [
70
+ 1,
71
+ // dot 1
72
+ 2,
73
+ // dot 2
74
+ 4,
75
+ // dot 3
76
+ 8,
77
+ // dot 4
78
+ 16,
79
+ // dot 5
80
+ 32,
81
+ // dot 6
82
+ 64,
83
+ // dot 7
84
+ 128
85
+ // dot 8
86
+ ];
64
87
  function normalize(coord) {
65
88
  return Math.round(coord);
66
89
  }
@@ -151,6 +174,60 @@ function setText(canvas, x, y, text) {
151
174
  rowMap.set(col + i, text[i]);
152
175
  }
153
176
  }
177
+ function braille(dots) {
178
+ let mask = 0;
179
+ for (let i = 0; i < 8; i++) {
180
+ if (dots[i]) {
181
+ mask |= DOT_MASKS[i];
182
+ }
183
+ }
184
+ return String.fromCharCode(BRAILLE_OFFSET + mask);
185
+ }
186
+ function brailleDots(...dots) {
187
+ let mask = 0;
188
+ for (const d of dots) {
189
+ if (d < 1 || d > 8) continue;
190
+ mask |= DOT_MASKS[d - 1];
191
+ }
192
+ return String.fromCharCode(BRAILLE_OFFSET + mask);
193
+ }
194
+ function brailleIcon(...chars) {
195
+ if (chars.length === 0 || chars.length > 3) return "";
196
+ const parts = [];
197
+ for (let i = 0; i < chars.length; i++) {
198
+ const dots = chars[i];
199
+ if (!dots) continue;
200
+ let mask = 0;
201
+ for (const d of dots) {
202
+ if (d < 1 || d > 8) continue;
203
+ mask |= DOT_MASKS[d - 1];
204
+ }
205
+ parts.push(String.fromCharCode(BRAILLE_OFFSET + mask));
206
+ }
207
+ return parts.join("");
208
+ }
209
+ function brailleGrid(pixels) {
210
+ let maxWidth = 0;
211
+ for (const row of pixels) {
212
+ if (row.length > maxWidth) maxWidth = row.length;
213
+ }
214
+ const height = Math.min(pixels.length, 4);
215
+ const width = Math.min(maxWidth, 6);
216
+ const charCount = Math.ceil(width / 2);
217
+ const masks = Array.from({ length: charCount }, () => 0);
218
+ for (let y = 0; y < height; y++) {
219
+ const row = pixels[y];
220
+ if (!row) continue;
221
+ for (let x = 0; x < width; x++) {
222
+ if (!row[x]) continue;
223
+ const charIdx = Math.floor(x / 2);
224
+ const mask = PIXEL_MAP[y]?.[x % 2];
225
+ if (mask === void 0) continue;
226
+ masks[charIdx] = (masks[charIdx] ?? 0) | mask;
227
+ }
228
+ }
229
+ return masks.map((m) => String.fromCharCode(BRAILLE_OFFSET + m)).join("");
230
+ }
154
231
  function rows(canvas, bounds) {
155
232
  if (canvas.chars.size === 0) return [];
156
233
  const allRows = [...canvas.chars.keys()];
@@ -381,8 +458,13 @@ function fixedFrame(canvas, delimiter = "\n") {
381
458
  // Annotate the CommonJS export names for ESM import in node:
382
459
  0 && (module.exports = {
383
460
  BRAILLE_OFFSET,
461
+ DOT_MASKS,
384
462
  PIXEL_MAP,
385
463
  animate,
464
+ braille,
465
+ brailleDots,
466
+ brailleGrid,
467
+ brailleIcon,
386
468
  clear,
387
469
  createCanvas,
388
470
  createFixedCanvas,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * btui — Braille TUI canvas for terminal drawing with Unicode braille characters.\n *\n * Braille dot layout per character cell:\n * ,___,\n * |1 4|\n * |2 5|\n * |3 6|\n * |7 8|\n * `````\n *\n * Each character maps to a 2x4 pixel grid.\n * Unicode braille characters: U+2800 to U+28FF.\n */\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst BRAILLE_OFFSET = 0x2800;\n\nconst PIXEL_MAP: readonly (readonly [number, number])[] = [\n\t[0x01, 0x08],\n\t[0x02, 0x10],\n\t[0x04, 0x20],\n\t[0x40, 0x80],\n] as const;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A single cell can hold a braille bitmask (number) or a text character (string). */\ntype CellValue = number | string;\n\n/** Sparse row → col → cell storage. */\ninterface CanvasChars {\n\treadonly [row: number]: {readonly [col: number]: CellValue} | undefined;\n}\n\n/** The braille canvas data structure. */\ninterface Canvas {\n\tchars: Map<number, Map<number, CellValue>>;\n\treadonly lineEnding: string;\n}\n\n/** Options for creating a canvas. */\ninterface CreateCanvasOptions {\n\treadonly lineEnding?: string;\n}\n\n/** Bounds for frame/rows rendering. */\ninterface FrameBounds {\n\treadonly minX?: number;\n\treadonly minY?: number;\n\treadonly maxX?: number;\n\treadonly maxY?: number;\n}\n\n/** A 2D point. */\ninterface Point {\n\treadonly x: number;\n\treadonly y: number;\n}\n\n/** Turtle graphics state. */\ninterface TurtleState {\n\treadonly canvas: Canvas;\n\tposX: number;\n\tposY: number;\n\trotation: number;\n\tbrushOn: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Coordinate helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a coordinate to an integer. */\nfunction normalize(coord: number): number {\n\treturn Math.round(coord);\n}\n\n/** Convert pixel (x, y) to cell (col, row). */\nfunction getPos(x: number, y: number): readonly [number, number] {\n\treturn [Math.floor(normalize(x) / 2), Math.floor(normalize(y) / 4)];\n}\n\n// ---------------------------------------------------------------------------\n// Canvas — creation & mutation\n// ---------------------------------------------------------------------------\n\n/** Create a new braille canvas. */\nfunction createCanvas(options?: CreateCanvasOptions): Canvas {\n\treturn {\n\t\tchars: new Map(),\n\t\tlineEnding: options?.lineEnding ?? '\\n',\n\t};\n}\n\n/** Remove all pixels from the canvas. */\nfunction clear(canvas: Canvas): void {\n\tcanvas.chars.clear();\n}\n\n/** Set (draw) a pixel at (x, y). */\nfunction set(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tlet rowMap = canvas.chars.get(row);\n\tif (!rowMap) {\n\t\trowMap = new Map();\n\t\tcanvas.chars.set(row, rowMap);\n\t}\n\n\tconst current = rowMap.get(col);\n\tif (current !== undefined && typeof current !== 'number') {\n\t\treturn;\n\t}\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\trowMap.set(col, ((current as number | undefined) ?? 0) | mask);\n}\n\n/** Unset (erase) a pixel at (x, y). */\nfunction unset(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tif (!rowMap) return;\n\n\tconst current = rowMap.get(col);\n\tif (current === undefined || typeof current !== 'number') return;\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\tconst updated = current & ~mask;\n\tif (updated === 0) {\n\t\trowMap.delete(col);\n\t\tif (rowMap.size === 0) {\n\t\t\tcanvas.chars.delete(row);\n\t\t}\n\t\treturn;\n\t}\n\n\trowMap.set(col, updated);\n}\n\n/** Toggle a pixel at (x, y). */\nfunction toggle(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tconst current = rowMap?.get(col);\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\tif (\n\t\tcurrent !== undefined &&\n\t\t(typeof current !== 'number' || (current & mask) !== 0)\n\t) {\n\t\tunset(canvas, x, y);\n\t} else {\n\t\tset(canvas, x, y);\n\t}\n}\n\n/** Get the state of a pixel at (x, y). Returns true if set. */\nfunction get(canvas: Canvas, x: number, y: number): boolean {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tif (!rowMap) return false;\n\n\tconst cell = rowMap.get(col);\n\tif (cell === undefined) return false;\n\tif (typeof cell !== 'number') return true;\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return false;\n\n\treturn (cell & mask) !== 0;\n}\n\n/** Set text at the given pixel coords. Each character occupies one cell column. */\nfunction setText(canvas: Canvas, x: number, y: number, text: string): void {\n\tconst [col, row] = getPos(x, y);\n\n\tlet rowMap = canvas.chars.get(row);\n\tif (!rowMap) {\n\t\trowMap = new Map();\n\t\tcanvas.chars.set(row, rowMap);\n\t}\n\n\tfor (let i = 0; i < text.length; i++) {\n\t\trowMap.set(col + i, text[i] as string);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Canvas — rendering\n// ---------------------------------------------------------------------------\n\n/** Return the canvas content as an array of strings (one per row). */\nfunction rows(canvas: Canvas, bounds?: FrameBounds): readonly string[] {\n\tif (canvas.chars.size === 0) return [];\n\n\tconst allRows = [...canvas.chars.keys()];\n\tconst minRow =\n\t\tbounds?.minY != null\n\t\t\t? Math.floor(bounds.minY / 4)\n\t\t\t: Math.min(...allRows);\n\tconst maxRow =\n\t\tbounds?.maxY != null\n\t\t\t? Math.floor((bounds.maxY - 1) / 4)\n\t\t\t: Math.max(...allRows);\n\n\t// Global min col for left alignment\n\tlet globalMinCol: number;\n\tif (bounds?.minX != null) {\n\t\tglobalMinCol = Math.floor(bounds.minX / 2);\n\t} else {\n\t\tlet min = Number.POSITIVE_INFINITY;\n\t\tfor (const rowMap of canvas.chars.values()) {\n\t\t\tfor (const c of rowMap.keys()) {\n\t\t\t\tif (c < min) min = c;\n\t\t\t}\n\t\t}\n\t\tglobalMinCol = min === Number.POSITIVE_INFINITY ? 0 : min;\n\t}\n\n\tconst result: string[] = [];\n\n\tfor (let rowNum = minRow; rowNum <= maxRow; rowNum++) {\n\t\tconst rowMap = canvas.chars.get(rowNum);\n\t\tif (!rowMap) {\n\t\t\tresult.push('');\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet maxCol: number;\n\t\tif (bounds?.maxX != null) {\n\t\t\tmaxCol = Math.floor((bounds.maxX - 1) / 2);\n\t\t} else {\n\t\t\tmaxCol = Math.max(...rowMap.keys());\n\t\t}\n\n\t\tconst chars: string[] = [];\n\t\tfor (let c = globalMinCol; c <= maxCol; c++) {\n\t\t\tconst cell = rowMap.get(c);\n\t\t\tif (cell === undefined || cell === 0) {\n\t\t\t\tchars.push(' ');\n\t\t\t} else if (typeof cell !== 'number') {\n\t\t\t\tchars.push(cell);\n\t\t\t} else {\n\t\t\t\tchars.push(String.fromCharCode(BRAILLE_OFFSET + cell));\n\t\t\t}\n\t\t}\n\n\t\tresult.push(chars.join(''));\n\t}\n\n\treturn result;\n}\n\n/** Render the canvas as a string. */\nfunction frame(canvas: Canvas, bounds?: FrameBounds): string {\n\treturn rows(canvas, bounds).join(canvas.lineEnding);\n}\n\n// ---------------------------------------------------------------------------\n// Drawing utilities — line\n// ---------------------------------------------------------------------------\n\n/** Generate coordinates along a line from (x1, y1) to (x2, y2) using Bresenham-style interpolation. */\nfunction* line(\n\tx1: number,\n\ty1: number,\n\tx2: number,\n\ty2: number,\n): Generator<Point, void, unknown> {\n\tconst nx1 = normalize(x1);\n\tconst ny1 = normalize(y1);\n\tconst nx2 = normalize(x2);\n\tconst ny2 = normalize(y2);\n\n\tconst xDiff = Math.abs(nx2 - nx1);\n\tconst yDiff = Math.abs(ny2 - ny1);\n\tconst xDir = nx1 <= nx2 ? 1 : -1;\n\tconst yDir = ny1 <= ny2 ? 1 : -1;\n\n\tconst steps = Math.max(xDiff, yDiff);\n\n\tfor (let i = 0; i <= steps; i++) {\n\t\tlet px = nx1;\n\t\tlet py = ny1;\n\n\t\tif (yDiff) {\n\t\t\tpy += Math.round((i * yDiff) / steps) * yDir;\n\t\t}\n\t\tif (xDiff) {\n\t\t\tpx += Math.round((i * xDiff) / steps) * xDir;\n\t\t}\n\n\t\tyield {x: px, y: py};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Drawing utilities — polygon\n// ---------------------------------------------------------------------------\n\n/** Generate coordinates for a regular polygon. */\nfunction* polygon(\n\tcenterX = 0,\n\tcenterY = 0,\n\tsides = 4,\n\tradius = 4,\n): Generator<Point, void, unknown> {\n\tconst degree = 360 / sides;\n\n\tfor (let n = 0; n < sides; n++) {\n\t\tconst a = n * degree;\n\t\tconst b = (n + 1) * degree;\n\t\tconst x1 =\n\t\t\t(centerX + Math.cos((a * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst y1 =\n\t\t\t(centerY + Math.sin((a * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst x2 =\n\t\t\t(centerX + Math.cos((b * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst y2 =\n\t\t\t(centerY + Math.sin((b * Math.PI) / 180)) * ((radius + 1) / 2);\n\n\t\tfor (const pt of line(x1, y1, x2, y2)) {\n\t\t\tyield pt;\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Turtle graphics\n// ---------------------------------------------------------------------------\n\n/** Create a new turtle state. */\nfunction createTurtle(posX = 0, posY = 0): TurtleState {\n\treturn {\n\t\tcanvas: createCanvas(),\n\t\tposX,\n\t\tposY,\n\t\trotation: 0,\n\t\tbrushOn: true,\n\t};\n}\n\n/** Pull the brush up (stop drawing on move). */\nfunction turtleUp(t: TurtleState): void {\n\tt.brushOn = false;\n}\n\n/** Push the brush down (draw on move). */\nfunction turtleDown(t: TurtleState): void {\n\tt.brushOn = true;\n}\n\n/** Move the turtle to an absolute position, drawing if the brush is down. */\nfunction turtleMove(t: TurtleState, x: number, y: number): void {\n\tif (t.brushOn) {\n\t\tfor (const pt of line(t.posX, t.posY, x, y)) {\n\t\t\tset(t.canvas, pt.x, pt.y);\n\t\t}\n\t}\n\tt.posX = x;\n\tt.posY = y;\n}\n\n/** Move the turtle forward by `step` pixels in its current direction. */\nfunction turtleForward(t: TurtleState, step: number): void {\n\tconst x = t.posX + Math.cos((t.rotation * Math.PI) / 180) * step;\n\tconst y = t.posY + Math.sin((t.rotation * Math.PI) / 180) * step;\n\tconst prevBrush = t.brushOn;\n\tt.brushOn = true;\n\tturtleMove(t, x, y);\n\tt.brushOn = prevBrush;\n}\n\n/** Move the turtle backward by `step` pixels. */\nfunction turtleBack(t: TurtleState, step: number): void {\n\tturtleForward(t, -step);\n}\n\n/** Rotate the turtle right (clockwise) by `angle` degrees. */\nfunction turtleRight(t: TurtleState, angle: number): void {\n\tt.rotation += angle;\n}\n\n/** Rotate the turtle left (counter-clockwise) by `angle` degrees. */\nfunction turtleLeft(t: TurtleState, angle: number): void {\n\tt.rotation -= angle;\n}\n\n// ---------------------------------------------------------------------------\n// Terminal size\n// ---------------------------------------------------------------------------\n\n/** Terminal dimensions in characters. */\ninterface TerminalSize {\n\treadonly width: number;\n\treadonly height: number;\n}\n\n/** Get terminal width and height. Falls back to 80x25. */\nfunction getTerminalSize(): TerminalSize {\n\tconst width = process.stdout?.columns ?? 80;\n\tconst height = process.stdout?.rows ?? 25;\n\treturn {width, height};\n}\n\n// ---------------------------------------------------------------------------\n// Animation\n// ---------------------------------------------------------------------------\n\n/** Options for the animate function. */\ninterface AnimateOptions {\n\treadonly delay?: number;\n\treadonly signal?: AbortSignal;\n}\n\n/** Frame generator yields arrays of points per frame. */\ntype FrameGenerator = Generator<readonly Point[], void, unknown>;\n\n/** Sleep for the given number of milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/** Run an animation loop. Clears the canvas each frame, draws points from the generator, and writes to stdout. */\nasync function animate(\n\tcanvas: Canvas,\n\tfn: () => FrameGenerator,\n\toptions?: AnimateOptions,\n): Promise<void> {\n\tconst delay = options?.delay ?? 1000 / 24;\n\n\tfor (const points of fn()) {\n\t\tif (options?.signal?.aborted) break;\n\n\t\tfor (const pt of points) {\n\t\t\tset(canvas, pt.x, pt.y);\n\t\t}\n\n\t\tconst f = frame(canvas);\n\t\tprocess.stdout.write(`\\x1b[H\\x1b[J${f}\\n`);\n\n\t\tif (delay > 0) {\n\t\t\tawait sleep(delay);\n\t\t}\n\n\t\tclear(canvas);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Fixed-size canvas (node-drawille style)\n// ---------------------------------------------------------------------------\n\n/** A fixed-size canvas backed by a Uint8Array buffer. */\ninterface FixedCanvas {\n\treadonly width: number;\n\treadonly height: number;\n\treadonly content: Uint8Array;\n}\n\n/** Create a fixed-size canvas with the given dimensions. Width rounds to multiple of 2, height to multiple of 4. */\nfunction createFixedCanvas(width: number, height: number): FixedCanvas {\n\tconst w = Math.floor(width / 2) * 2;\n\tconst h = Math.floor(height / 4) * 4;\n\treturn {\n\t\twidth: w,\n\t\theight: h,\n\t\tcontent: new Uint8Array((w * h) / 8),\n\t};\n}\n\n/** Clear all pixels from a fixed canvas. */\nfunction fixedClear(canvas: FixedCanvas): void {\n\tcanvas.content.fill(0);\n}\n\n/** Set a pixel on a fixed canvas. */\nfunction fixedSet(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val | mask;\n}\n\n/** Unset a pixel on a fixed canvas. */\nfunction fixedUnset(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val & ~mask;\n}\n\n/** Toggle a pixel on a fixed canvas. */\nfunction fixedToggle(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val ^ mask;\n}\n\n/** Get the state of a pixel on a fixed canvas. */\nfunction fixedGet(canvas: FixedCanvas, x: number, y: number): boolean {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height))\n\t\treturn false;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return false;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return false;\n\treturn (val & mask) !== 0;\n}\n\n/** Render a fixed canvas as a string. */\nfunction fixedFrame(canvas: FixedCanvas, delimiter = '\\n'): string {\n\tconst frameWidth = canvas.width / 2;\n\tconst parts: string[] = [];\n\n\tfor (let i = 0; i < canvas.content.length; i++) {\n\t\tif (i % frameWidth === 0) {\n\t\t\tparts.push(delimiter);\n\t\t}\n\t\tconst val = canvas.content[i];\n\t\tparts.push(val ? String.fromCharCode(BRAILLE_OFFSET + val) : ' ');\n\t}\n\tparts.push(delimiter);\n\n\treturn parts.join('');\n}\n\n// ---------------------------------------------------------------------------\n// Exports\n// ---------------------------------------------------------------------------\n\nexport {\n\t// Constants\n\tBRAILLE_OFFSET,\n\tPIXEL_MAP,\n\t// Types\n\ttype AnimateOptions,\n\ttype Canvas,\n\ttype CanvasChars,\n\ttype CellValue,\n\ttype CreateCanvasOptions,\n\ttype FixedCanvas,\n\ttype FrameBounds,\n\ttype FrameGenerator,\n\ttype Point,\n\ttype TerminalSize,\n\ttype TurtleState,\n\t// Coordinate helpers\n\tnormalize,\n\tgetPos,\n\t// Terminal\n\tgetTerminalSize,\n\t// Sparse canvas\n\tcreateCanvas,\n\tclear,\n\tset,\n\tunset,\n\ttoggle,\n\tget,\n\tsetText,\n\trows,\n\tframe,\n\t// Animation\n\tanimate,\n\t// Drawing utilities\n\tline,\n\tpolygon,\n\t// Turtle\n\tcreateTurtle,\n\tturtleUp,\n\tturtleDown,\n\tturtleMove,\n\tturtleForward,\n\tturtleBack,\n\tturtleRight,\n\tturtleLeft,\n\t// Fixed canvas\n\tcreateFixedCanvas,\n\tfixedClear,\n\tfixedSet,\n\tfixedUnset,\n\tfixedToggle,\n\tfixedGet,\n\tfixedFrame,\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * btui — Braille TUI canvas for terminal drawing with Unicode braille characters.\n *\n * Braille dot layout per character cell:\n * ,___,\n * |1 4|\n * |2 5|\n * |3 6|\n * |7 8|\n * `````\n *\n * Each character maps to a 2x4 pixel grid.\n * Unicode braille characters: U+2800 to U+28FF.\n */\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst BRAILLE_OFFSET = 0x2800;\n\nconst PIXEL_MAP: readonly (readonly [number, number])[] = [\n\t[0x01, 0x08],\n\t[0x02, 0x10],\n\t[0x04, 0x20],\n\t[0x40, 0x80],\n] as const;\n\n/** Bitmask for each braille dot number (1–8). */\nconst DOT_MASKS: readonly number[] = [\n\t0x01, // dot 1\n\t0x02, // dot 2\n\t0x04, // dot 3\n\t0x08, // dot 4\n\t0x10, // dot 5\n\t0x20, // dot 6\n\t0x40, // dot 7\n\t0x80, // dot 8\n] as const;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A single cell can hold a braille bitmask (number) or a text character (string). */\ntype CellValue = number | string;\n\n/** Sparse row → col → cell storage. */\ninterface CanvasChars {\n\treadonly [row: number]: {readonly [col: number]: CellValue} | undefined;\n}\n\n/** The braille canvas data structure. */\ninterface Canvas {\n\tchars: Map<number, Map<number, CellValue>>;\n\treadonly lineEnding: string;\n}\n\n/** Options for creating a canvas. */\ninterface CreateCanvasOptions {\n\treadonly lineEnding?: string;\n}\n\n/** Bounds for frame/rows rendering. */\ninterface FrameBounds {\n\treadonly minX?: number;\n\treadonly minY?: number;\n\treadonly maxX?: number;\n\treadonly maxY?: number;\n}\n\n/** A 2D point. */\ninterface Point {\n\treadonly x: number;\n\treadonly y: number;\n}\n\n/** Turtle graphics state. */\ninterface TurtleState {\n\treadonly canvas: Canvas;\n\tposX: number;\n\tposY: number;\n\trotation: number;\n\tbrushOn: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Coordinate helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a coordinate to an integer. */\nfunction normalize(coord: number): number {\n\treturn Math.round(coord);\n}\n\n/** Convert pixel (x, y) to cell (col, row). */\nfunction getPos(x: number, y: number): readonly [number, number] {\n\treturn [Math.floor(normalize(x) / 2), Math.floor(normalize(y) / 4)];\n}\n\n// ---------------------------------------------------------------------------\n// Canvas — creation & mutation\n// ---------------------------------------------------------------------------\n\n/** Create a new braille canvas. */\nfunction createCanvas(options?: CreateCanvasOptions): Canvas {\n\treturn {\n\t\tchars: new Map(),\n\t\tlineEnding: options?.lineEnding ?? '\\n',\n\t};\n}\n\n/** Remove all pixels from the canvas. */\nfunction clear(canvas: Canvas): void {\n\tcanvas.chars.clear();\n}\n\n/** Set (draw) a pixel at (x, y). */\nfunction set(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tlet rowMap = canvas.chars.get(row);\n\tif (!rowMap) {\n\t\trowMap = new Map();\n\t\tcanvas.chars.set(row, rowMap);\n\t}\n\n\tconst current = rowMap.get(col);\n\tif (current !== undefined && typeof current !== 'number') {\n\t\treturn;\n\t}\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\trowMap.set(col, ((current as number | undefined) ?? 0) | mask);\n}\n\n/** Unset (erase) a pixel at (x, y). */\nfunction unset(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tif (!rowMap) return;\n\n\tconst current = rowMap.get(col);\n\tif (current === undefined || typeof current !== 'number') return;\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\tconst updated = current & ~mask;\n\tif (updated === 0) {\n\t\trowMap.delete(col);\n\t\tif (rowMap.size === 0) {\n\t\t\tcanvas.chars.delete(row);\n\t\t}\n\t\treturn;\n\t}\n\n\trowMap.set(col, updated);\n}\n\n/** Toggle a pixel at (x, y). */\nfunction toggle(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tconst current = rowMap?.get(col);\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\tif (\n\t\tcurrent !== undefined &&\n\t\t(typeof current !== 'number' || (current & mask) !== 0)\n\t) {\n\t\tunset(canvas, x, y);\n\t} else {\n\t\tset(canvas, x, y);\n\t}\n}\n\n/** Get the state of a pixel at (x, y). Returns true if set. */\nfunction get(canvas: Canvas, x: number, y: number): boolean {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tif (!rowMap) return false;\n\n\tconst cell = rowMap.get(col);\n\tif (cell === undefined) return false;\n\tif (typeof cell !== 'number') return true;\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return false;\n\n\treturn (cell & mask) !== 0;\n}\n\n/** Set text at the given pixel coords. Each character occupies one cell column. */\nfunction setText(canvas: Canvas, x: number, y: number, text: string): void {\n\tconst [col, row] = getPos(x, y);\n\n\tlet rowMap = canvas.chars.get(row);\n\tif (!rowMap) {\n\t\trowMap = new Map();\n\t\tcanvas.chars.set(row, rowMap);\n\t}\n\n\tfor (let i = 0; i < text.length; i++) {\n\t\trowMap.set(col + i, text[i] as string);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Braille character builders\n// ---------------------------------------------------------------------------\n\n/**\n * 8-element tuple: on/off state for dots 1–8.\n *\n * ,___,\n * |1 4|\n * |2 5|\n * |3 6|\n * |7 8|\n * `````\n */\ntype DotPattern = readonly [\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n];\n\n/** Build a braille character from an 8-element on/off pattern (dots 1–8). */\nfunction braille(dots: DotPattern): string {\n\tlet mask = 0;\n\tfor (let i = 0; i < 8; i++) {\n\t\tif (dots[i]) {\n\t\t\tmask |= DOT_MASKS[i] as number;\n\t\t}\n\t}\n\treturn String.fromCharCode(BRAILLE_OFFSET + mask);\n}\n\n/** Build a braille character by listing which dot numbers (1–8) are on. */\nfunction brailleDots(...dots: readonly number[]): string {\n\tlet mask = 0;\n\tfor (const d of dots) {\n\t\tif (d < 1 || d > 8) continue;\n\t\tmask |= DOT_MASKS[d - 1] as number;\n\t}\n\treturn String.fromCharCode(BRAILLE_OFFSET + mask);\n}\n\n/**\n * Build a 1–3 character braille icon. Each argument is an array of\n * dot numbers (1–8) that are ON for that character position.\n *\n * @example\n * brailleIcon([1, 2, 3, 7], [4, 5, 6, 8]) // \"⡇⢸\" — left bar + right bar\n * brailleIcon([1, 4]) // \"⠉\" — single char, top row\n * brailleIcon([1], [1, 8], [1]) // 3-char\n */\nfunction brailleIcon(...chars: readonly (readonly number[])[]): string {\n\tif (chars.length === 0 || chars.length > 3) return '';\n\n\tconst parts: string[] = [];\n\tfor (let i = 0; i < chars.length; i++) {\n\t\tconst dots = chars[i];\n\t\tif (!dots) continue;\n\t\tlet mask = 0;\n\t\tfor (const d of dots) {\n\t\t\tif (d < 1 || d > 8) continue;\n\t\t\tmask |= DOT_MASKS[d - 1] as number;\n\t\t}\n\t\tparts.push(String.fromCharCode(BRAILLE_OFFSET + mask));\n\t}\n\treturn parts.join('');\n}\n\n/**\n * Build a braille icon from a visual pixel grid.\n * Each row is an array of 0/1 values. Max 6 columns wide × 4 rows tall.\n * Every 2 columns map to one braille character.\n *\n * col: 0 1 │ 2 3 │ 4 5\n * ────┼─────┼────\n * row0: 1 4 │ 1 4 │ 1 4\n * row1: 2 5 │ 2 5 │ 2 5\n * row2: 3 6 │ 3 6 │ 3 6\n * row3: 7 8 │ 7 8 │ 7 8\n * ─ch0─ ─ch1─ ─ch2─\n *\n * @example\n * brailleGrid([\n * [0, 1, 1, 0],\n * [1, 0, 0, 1],\n * [1, 0, 0, 1],\n * [0, 1, 1, 0],\n * ]) // \"⠳⠞\" — a diamond shape, 2 chars wide\n */\nfunction brailleGrid(pixels: readonly (readonly number[])[]): string {\n\tlet maxWidth = 0;\n\tfor (const row of pixels) {\n\t\tif (row.length > maxWidth) maxWidth = row.length;\n\t}\n\n\tconst height = Math.min(pixels.length, 4);\n\tconst width = Math.min(maxWidth, 6);\n\tconst charCount = Math.ceil(width / 2);\n\n\tconst masks: number[] = Array.from({length: charCount}, () => 0);\n\n\tfor (let y = 0; y < height; y++) {\n\t\tconst row = pixels[y];\n\t\tif (!row) continue;\n\t\tfor (let x = 0; x < width; x++) {\n\t\t\tif (!row[x]) continue;\n\t\t\tconst charIdx = Math.floor(x / 2);\n\t\t\tconst mask = PIXEL_MAP[y]?.[x % 2];\n\t\t\tif (mask === undefined) continue;\n\t\t\tmasks[charIdx] = (masks[charIdx] ?? 0) | mask;\n\t\t}\n\t}\n\n\treturn masks.map(m => String.fromCharCode(BRAILLE_OFFSET + m)).join('');\n}\n\n// ---------------------------------------------------------------------------\n// Canvas — rendering\n// ---------------------------------------------------------------------------\n\n/** Return the canvas content as an array of strings (one per row). */\nfunction rows(canvas: Canvas, bounds?: FrameBounds): readonly string[] {\n\tif (canvas.chars.size === 0) return [];\n\n\tconst allRows = [...canvas.chars.keys()];\n\tconst minRow =\n\t\tbounds?.minY != null\n\t\t\t? Math.floor(bounds.minY / 4)\n\t\t\t: Math.min(...allRows);\n\tconst maxRow =\n\t\tbounds?.maxY != null\n\t\t\t? Math.floor((bounds.maxY - 1) / 4)\n\t\t\t: Math.max(...allRows);\n\n\t// Global min col for left alignment\n\tlet globalMinCol: number;\n\tif (bounds?.minX != null) {\n\t\tglobalMinCol = Math.floor(bounds.minX / 2);\n\t} else {\n\t\tlet min = Number.POSITIVE_INFINITY;\n\t\tfor (const rowMap of canvas.chars.values()) {\n\t\t\tfor (const c of rowMap.keys()) {\n\t\t\t\tif (c < min) min = c;\n\t\t\t}\n\t\t}\n\t\tglobalMinCol = min === Number.POSITIVE_INFINITY ? 0 : min;\n\t}\n\n\tconst result: string[] = [];\n\n\tfor (let rowNum = minRow; rowNum <= maxRow; rowNum++) {\n\t\tconst rowMap = canvas.chars.get(rowNum);\n\t\tif (!rowMap) {\n\t\t\tresult.push('');\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet maxCol: number;\n\t\tif (bounds?.maxX != null) {\n\t\t\tmaxCol = Math.floor((bounds.maxX - 1) / 2);\n\t\t} else {\n\t\t\tmaxCol = Math.max(...rowMap.keys());\n\t\t}\n\n\t\tconst chars: string[] = [];\n\t\tfor (let c = globalMinCol; c <= maxCol; c++) {\n\t\t\tconst cell = rowMap.get(c);\n\t\t\tif (cell === undefined || cell === 0) {\n\t\t\t\tchars.push(' ');\n\t\t\t} else if (typeof cell !== 'number') {\n\t\t\t\tchars.push(cell);\n\t\t\t} else {\n\t\t\t\tchars.push(String.fromCharCode(BRAILLE_OFFSET + cell));\n\t\t\t}\n\t\t}\n\n\t\tresult.push(chars.join(''));\n\t}\n\n\treturn result;\n}\n\n/** Render the canvas as a string. */\nfunction frame(canvas: Canvas, bounds?: FrameBounds): string {\n\treturn rows(canvas, bounds).join(canvas.lineEnding);\n}\n\n// ---------------------------------------------------------------------------\n// Drawing utilities — line\n// ---------------------------------------------------------------------------\n\n/** Generate coordinates along a line from (x1, y1) to (x2, y2) using Bresenham-style interpolation. */\nfunction* line(\n\tx1: number,\n\ty1: number,\n\tx2: number,\n\ty2: number,\n): Generator<Point, void, unknown> {\n\tconst nx1 = normalize(x1);\n\tconst ny1 = normalize(y1);\n\tconst nx2 = normalize(x2);\n\tconst ny2 = normalize(y2);\n\n\tconst xDiff = Math.abs(nx2 - nx1);\n\tconst yDiff = Math.abs(ny2 - ny1);\n\tconst xDir = nx1 <= nx2 ? 1 : -1;\n\tconst yDir = ny1 <= ny2 ? 1 : -1;\n\n\tconst steps = Math.max(xDiff, yDiff);\n\n\tfor (let i = 0; i <= steps; i++) {\n\t\tlet px = nx1;\n\t\tlet py = ny1;\n\n\t\tif (yDiff) {\n\t\t\tpy += Math.round((i * yDiff) / steps) * yDir;\n\t\t}\n\t\tif (xDiff) {\n\t\t\tpx += Math.round((i * xDiff) / steps) * xDir;\n\t\t}\n\n\t\tyield {x: px, y: py};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Drawing utilities — polygon\n// ---------------------------------------------------------------------------\n\n/** Generate coordinates for a regular polygon. */\nfunction* polygon(\n\tcenterX = 0,\n\tcenterY = 0,\n\tsides = 4,\n\tradius = 4,\n): Generator<Point, void, unknown> {\n\tconst degree = 360 / sides;\n\n\tfor (let n = 0; n < sides; n++) {\n\t\tconst a = n * degree;\n\t\tconst b = (n + 1) * degree;\n\t\tconst x1 =\n\t\t\t(centerX + Math.cos((a * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst y1 =\n\t\t\t(centerY + Math.sin((a * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst x2 =\n\t\t\t(centerX + Math.cos((b * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst y2 =\n\t\t\t(centerY + Math.sin((b * Math.PI) / 180)) * ((radius + 1) / 2);\n\n\t\tfor (const pt of line(x1, y1, x2, y2)) {\n\t\t\tyield pt;\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Turtle graphics\n// ---------------------------------------------------------------------------\n\n/** Create a new turtle state. */\nfunction createTurtle(posX = 0, posY = 0): TurtleState {\n\treturn {\n\t\tcanvas: createCanvas(),\n\t\tposX,\n\t\tposY,\n\t\trotation: 0,\n\t\tbrushOn: true,\n\t};\n}\n\n/** Pull the brush up (stop drawing on move). */\nfunction turtleUp(t: TurtleState): void {\n\tt.brushOn = false;\n}\n\n/** Push the brush down (draw on move). */\nfunction turtleDown(t: TurtleState): void {\n\tt.brushOn = true;\n}\n\n/** Move the turtle to an absolute position, drawing if the brush is down. */\nfunction turtleMove(t: TurtleState, x: number, y: number): void {\n\tif (t.brushOn) {\n\t\tfor (const pt of line(t.posX, t.posY, x, y)) {\n\t\t\tset(t.canvas, pt.x, pt.y);\n\t\t}\n\t}\n\tt.posX = x;\n\tt.posY = y;\n}\n\n/** Move the turtle forward by `step` pixels in its current direction. */\nfunction turtleForward(t: TurtleState, step: number): void {\n\tconst x = t.posX + Math.cos((t.rotation * Math.PI) / 180) * step;\n\tconst y = t.posY + Math.sin((t.rotation * Math.PI) / 180) * step;\n\tconst prevBrush = t.brushOn;\n\tt.brushOn = true;\n\tturtleMove(t, x, y);\n\tt.brushOn = prevBrush;\n}\n\n/** Move the turtle backward by `step` pixels. */\nfunction turtleBack(t: TurtleState, step: number): void {\n\tturtleForward(t, -step);\n}\n\n/** Rotate the turtle right (clockwise) by `angle` degrees. */\nfunction turtleRight(t: TurtleState, angle: number): void {\n\tt.rotation += angle;\n}\n\n/** Rotate the turtle left (counter-clockwise) by `angle` degrees. */\nfunction turtleLeft(t: TurtleState, angle: number): void {\n\tt.rotation -= angle;\n}\n\n// ---------------------------------------------------------------------------\n// Terminal size\n// ---------------------------------------------------------------------------\n\n/** Terminal dimensions in characters. */\ninterface TerminalSize {\n\treadonly width: number;\n\treadonly height: number;\n}\n\n/** Get terminal width and height. Falls back to 80x25. */\nfunction getTerminalSize(): TerminalSize {\n\tconst width = process.stdout?.columns ?? 80;\n\tconst height = process.stdout?.rows ?? 25;\n\treturn {width, height};\n}\n\n// ---------------------------------------------------------------------------\n// Animation\n// ---------------------------------------------------------------------------\n\n/** Options for the animate function. */\ninterface AnimateOptions {\n\treadonly delay?: number;\n\treadonly signal?: AbortSignal;\n}\n\n/** Frame generator yields arrays of points per frame. */\ntype FrameGenerator = Generator<readonly Point[], void, unknown>;\n\n/** Sleep for the given number of milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/** Run an animation loop. Clears the canvas each frame, draws points from the generator, and writes to stdout. */\nasync function animate(\n\tcanvas: Canvas,\n\tfn: () => FrameGenerator,\n\toptions?: AnimateOptions,\n): Promise<void> {\n\tconst delay = options?.delay ?? 1000 / 24;\n\n\tfor (const points of fn()) {\n\t\tif (options?.signal?.aborted) break;\n\n\t\tfor (const pt of points) {\n\t\t\tset(canvas, pt.x, pt.y);\n\t\t}\n\n\t\tconst f = frame(canvas);\n\t\tprocess.stdout.write(`\\x1b[H\\x1b[J${f}\\n`);\n\n\t\tif (delay > 0) {\n\t\t\tawait sleep(delay);\n\t\t}\n\n\t\tclear(canvas);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Fixed-size canvas (node-drawille style)\n// ---------------------------------------------------------------------------\n\n/** A fixed-size canvas backed by a Uint8Array buffer. */\ninterface FixedCanvas {\n\treadonly width: number;\n\treadonly height: number;\n\treadonly content: Uint8Array;\n}\n\n/** Create a fixed-size canvas with the given dimensions. Width rounds to multiple of 2, height to multiple of 4. */\nfunction createFixedCanvas(width: number, height: number): FixedCanvas {\n\tconst w = Math.floor(width / 2) * 2;\n\tconst h = Math.floor(height / 4) * 4;\n\treturn {\n\t\twidth: w,\n\t\theight: h,\n\t\tcontent: new Uint8Array((w * h) / 8),\n\t};\n}\n\n/** Clear all pixels from a fixed canvas. */\nfunction fixedClear(canvas: FixedCanvas): void {\n\tcanvas.content.fill(0);\n}\n\n/** Set a pixel on a fixed canvas. */\nfunction fixedSet(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val | mask;\n}\n\n/** Unset a pixel on a fixed canvas. */\nfunction fixedUnset(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val & ~mask;\n}\n\n/** Toggle a pixel on a fixed canvas. */\nfunction fixedToggle(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val ^ mask;\n}\n\n/** Get the state of a pixel on a fixed canvas. */\nfunction fixedGet(canvas: FixedCanvas, x: number, y: number): boolean {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height))\n\t\treturn false;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return false;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return false;\n\treturn (val & mask) !== 0;\n}\n\n/** Render a fixed canvas as a string. */\nfunction fixedFrame(canvas: FixedCanvas, delimiter = '\\n'): string {\n\tconst frameWidth = canvas.width / 2;\n\tconst parts: string[] = [];\n\n\tfor (let i = 0; i < canvas.content.length; i++) {\n\t\tif (i % frameWidth === 0) {\n\t\t\tparts.push(delimiter);\n\t\t}\n\t\tconst val = canvas.content[i];\n\t\tparts.push(val ? String.fromCharCode(BRAILLE_OFFSET + val) : ' ');\n\t}\n\tparts.push(delimiter);\n\n\treturn parts.join('');\n}\n\n// ---------------------------------------------------------------------------\n// Exports\n// ---------------------------------------------------------------------------\n\nexport {\n\t// Constants\n\tBRAILLE_OFFSET,\n\tPIXEL_MAP,\n\tDOT_MASKS,\n\t// Types\n\ttype AnimateOptions,\n\ttype Canvas,\n\ttype CanvasChars,\n\ttype CellValue,\n\ttype CreateCanvasOptions,\n\ttype DotPattern,\n\ttype FixedCanvas,\n\ttype FrameBounds,\n\ttype FrameGenerator,\n\ttype Point,\n\ttype TerminalSize,\n\ttype TurtleState,\n\t// Coordinate helpers\n\tnormalize,\n\tgetPos,\n\t// Terminal\n\tgetTerminalSize,\n\t// Braille builders\n\tbraille,\n\tbrailleDots,\n\tbrailleIcon,\n\tbrailleGrid,\n\t// Sparse canvas\n\tcreateCanvas,\n\tclear,\n\tset,\n\tunset,\n\ttoggle,\n\tget,\n\tsetText,\n\trows,\n\tframe,\n\t// Animation\n\tanimate,\n\t// Drawing utilities\n\tline,\n\tpolygon,\n\t// Turtle\n\tcreateTurtle,\n\tturtleUp,\n\tturtleDown,\n\tturtleMove,\n\tturtleForward,\n\tturtleBack,\n\tturtleRight,\n\tturtleLeft,\n\t// Fixed canvas\n\tcreateFixedCanvas,\n\tfixedClear,\n\tfixedSet,\n\tfixedUnset,\n\tfixedToggle,\n\tfixedGet,\n\tfixedFrame,\n};\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -14,6 +14,8 @@
14
14
  */
15
15
  declare const BRAILLE_OFFSET = 10240;
16
16
  declare const PIXEL_MAP: readonly (readonly [number, number])[];
17
+ /** Bitmask for each braille dot number (1–8). */
18
+ declare const DOT_MASKS: readonly number[];
17
19
  /** A single cell can hold a braille bitmask (number) or a text character (string). */
18
20
  type CellValue = number | string;
19
21
  /** Sparse row → col → cell storage. */
@@ -69,6 +71,62 @@ declare function toggle(canvas: Canvas, x: number, y: number): void;
69
71
  declare function get(canvas: Canvas, x: number, y: number): boolean;
70
72
  /** Set text at the given pixel coords. Each character occupies one cell column. */
71
73
  declare function setText(canvas: Canvas, x: number, y: number, text: string): void;
74
+ /**
75
+ * 8-element tuple: on/off state for dots 1–8.
76
+ *
77
+ * ,___,
78
+ * |1 4|
79
+ * |2 5|
80
+ * |3 6|
81
+ * |7 8|
82
+ * `````
83
+ */
84
+ type DotPattern = readonly [
85
+ boolean,
86
+ boolean,
87
+ boolean,
88
+ boolean,
89
+ boolean,
90
+ boolean,
91
+ boolean,
92
+ boolean
93
+ ];
94
+ /** Build a braille character from an 8-element on/off pattern (dots 1–8). */
95
+ declare function braille(dots: DotPattern): string;
96
+ /** Build a braille character by listing which dot numbers (1–8) are on. */
97
+ declare function brailleDots(...dots: readonly number[]): string;
98
+ /**
99
+ * Build a 1–3 character braille icon. Each argument is an array of
100
+ * dot numbers (1–8) that are ON for that character position.
101
+ *
102
+ * @example
103
+ * brailleIcon([1, 2, 3, 7], [4, 5, 6, 8]) // "⡇⢸" — left bar + right bar
104
+ * brailleIcon([1, 4]) // "⠉" — single char, top row
105
+ * brailleIcon([1], [1, 8], [1]) // 3-char
106
+ */
107
+ declare function brailleIcon(...chars: readonly (readonly number[])[]): string;
108
+ /**
109
+ * Build a braille icon from a visual pixel grid.
110
+ * Each row is an array of 0/1 values. Max 6 columns wide × 4 rows tall.
111
+ * Every 2 columns map to one braille character.
112
+ *
113
+ * col: 0 1 │ 2 3 │ 4 5
114
+ * ────┼─────┼────
115
+ * row0: 1 4 │ 1 4 │ 1 4
116
+ * row1: 2 5 │ 2 5 │ 2 5
117
+ * row2: 3 6 │ 3 6 │ 3 6
118
+ * row3: 7 8 │ 7 8 │ 7 8
119
+ * ─ch0─ ─ch1─ ─ch2─
120
+ *
121
+ * @example
122
+ * brailleGrid([
123
+ * [0, 1, 1, 0],
124
+ * [1, 0, 0, 1],
125
+ * [1, 0, 0, 1],
126
+ * [0, 1, 1, 0],
127
+ * ]) // "⠳⠞" — a diamond shape, 2 chars wide
128
+ */
129
+ declare function brailleGrid(pixels: readonly (readonly number[])[]): string;
72
130
  /** Return the canvas content as an array of strings (one per row). */
73
131
  declare function rows(canvas: Canvas, bounds?: FrameBounds): readonly string[];
74
132
  /** Render the canvas as a string. */
@@ -130,4 +188,4 @@ declare function fixedGet(canvas: FixedCanvas, x: number, y: number): boolean;
130
188
  /** Render a fixed canvas as a string. */
131
189
  declare function fixedFrame(canvas: FixedCanvas, delimiter?: string): string;
132
190
 
133
- export { type AnimateOptions, BRAILLE_OFFSET, type Canvas, type CanvasChars, type CellValue, type CreateCanvasOptions, type FixedCanvas, type FrameBounds, type FrameGenerator, PIXEL_MAP, type Point, type TerminalSize, type TurtleState, animate, clear, createCanvas, createFixedCanvas, createTurtle, fixedClear, fixedFrame, fixedGet, fixedSet, fixedToggle, fixedUnset, frame, get, getPos, getTerminalSize, line, normalize, polygon, rows, set, setText, toggle, turtleBack, turtleDown, turtleForward, turtleLeft, turtleMove, turtleRight, turtleUp, unset };
191
+ export { type AnimateOptions, BRAILLE_OFFSET, type Canvas, type CanvasChars, type CellValue, type CreateCanvasOptions, DOT_MASKS, type DotPattern, type FixedCanvas, type FrameBounds, type FrameGenerator, PIXEL_MAP, type Point, type TerminalSize, type TurtleState, animate, braille, brailleDots, brailleGrid, brailleIcon, clear, createCanvas, createFixedCanvas, createTurtle, fixedClear, fixedFrame, fixedGet, fixedSet, fixedToggle, fixedUnset, frame, get, getPos, getTerminalSize, line, normalize, polygon, rows, set, setText, toggle, turtleBack, turtleDown, turtleForward, turtleLeft, turtleMove, turtleRight, turtleUp, unset };
package/dist/index.d.ts CHANGED
@@ -14,6 +14,8 @@
14
14
  */
15
15
  declare const BRAILLE_OFFSET = 10240;
16
16
  declare const PIXEL_MAP: readonly (readonly [number, number])[];
17
+ /** Bitmask for each braille dot number (1–8). */
18
+ declare const DOT_MASKS: readonly number[];
17
19
  /** A single cell can hold a braille bitmask (number) or a text character (string). */
18
20
  type CellValue = number | string;
19
21
  /** Sparse row → col → cell storage. */
@@ -69,6 +71,62 @@ declare function toggle(canvas: Canvas, x: number, y: number): void;
69
71
  declare function get(canvas: Canvas, x: number, y: number): boolean;
70
72
  /** Set text at the given pixel coords. Each character occupies one cell column. */
71
73
  declare function setText(canvas: Canvas, x: number, y: number, text: string): void;
74
+ /**
75
+ * 8-element tuple: on/off state for dots 1–8.
76
+ *
77
+ * ,___,
78
+ * |1 4|
79
+ * |2 5|
80
+ * |3 6|
81
+ * |7 8|
82
+ * `````
83
+ */
84
+ type DotPattern = readonly [
85
+ boolean,
86
+ boolean,
87
+ boolean,
88
+ boolean,
89
+ boolean,
90
+ boolean,
91
+ boolean,
92
+ boolean
93
+ ];
94
+ /** Build a braille character from an 8-element on/off pattern (dots 1–8). */
95
+ declare function braille(dots: DotPattern): string;
96
+ /** Build a braille character by listing which dot numbers (1–8) are on. */
97
+ declare function brailleDots(...dots: readonly number[]): string;
98
+ /**
99
+ * Build a 1–3 character braille icon. Each argument is an array of
100
+ * dot numbers (1–8) that are ON for that character position.
101
+ *
102
+ * @example
103
+ * brailleIcon([1, 2, 3, 7], [4, 5, 6, 8]) // "⡇⢸" — left bar + right bar
104
+ * brailleIcon([1, 4]) // "⠉" — single char, top row
105
+ * brailleIcon([1], [1, 8], [1]) // 3-char
106
+ */
107
+ declare function brailleIcon(...chars: readonly (readonly number[])[]): string;
108
+ /**
109
+ * Build a braille icon from a visual pixel grid.
110
+ * Each row is an array of 0/1 values. Max 6 columns wide × 4 rows tall.
111
+ * Every 2 columns map to one braille character.
112
+ *
113
+ * col: 0 1 │ 2 3 │ 4 5
114
+ * ────┼─────┼────
115
+ * row0: 1 4 │ 1 4 │ 1 4
116
+ * row1: 2 5 │ 2 5 │ 2 5
117
+ * row2: 3 6 │ 3 6 │ 3 6
118
+ * row3: 7 8 │ 7 8 │ 7 8
119
+ * ─ch0─ ─ch1─ ─ch2─
120
+ *
121
+ * @example
122
+ * brailleGrid([
123
+ * [0, 1, 1, 0],
124
+ * [1, 0, 0, 1],
125
+ * [1, 0, 0, 1],
126
+ * [0, 1, 1, 0],
127
+ * ]) // "⠳⠞" — a diamond shape, 2 chars wide
128
+ */
129
+ declare function brailleGrid(pixels: readonly (readonly number[])[]): string;
72
130
  /** Return the canvas content as an array of strings (one per row). */
73
131
  declare function rows(canvas: Canvas, bounds?: FrameBounds): readonly string[];
74
132
  /** Render the canvas as a string. */
@@ -130,4 +188,4 @@ declare function fixedGet(canvas: FixedCanvas, x: number, y: number): boolean;
130
188
  /** Render a fixed canvas as a string. */
131
189
  declare function fixedFrame(canvas: FixedCanvas, delimiter?: string): string;
132
190
 
133
- export { type AnimateOptions, BRAILLE_OFFSET, type Canvas, type CanvasChars, type CellValue, type CreateCanvasOptions, type FixedCanvas, type FrameBounds, type FrameGenerator, PIXEL_MAP, type Point, type TerminalSize, type TurtleState, animate, clear, createCanvas, createFixedCanvas, createTurtle, fixedClear, fixedFrame, fixedGet, fixedSet, fixedToggle, fixedUnset, frame, get, getPos, getTerminalSize, line, normalize, polygon, rows, set, setText, toggle, turtleBack, turtleDown, turtleForward, turtleLeft, turtleMove, turtleRight, turtleUp, unset };
191
+ export { type AnimateOptions, BRAILLE_OFFSET, type Canvas, type CanvasChars, type CellValue, type CreateCanvasOptions, DOT_MASKS, type DotPattern, type FixedCanvas, type FrameBounds, type FrameGenerator, PIXEL_MAP, type Point, type TerminalSize, type TurtleState, animate, braille, brailleDots, brailleGrid, brailleIcon, clear, createCanvas, createFixedCanvas, createTurtle, fixedClear, fixedFrame, fixedGet, fixedSet, fixedToggle, fixedUnset, frame, get, getPos, getTerminalSize, line, normalize, polygon, rows, set, setText, toggle, turtleBack, turtleDown, turtleForward, turtleLeft, turtleMove, turtleRight, turtleUp, unset };
package/dist/index.js CHANGED
@@ -6,6 +6,24 @@ var PIXEL_MAP = [
6
6
  [4, 32],
7
7
  [64, 128]
8
8
  ];
9
+ var DOT_MASKS = [
10
+ 1,
11
+ // dot 1
12
+ 2,
13
+ // dot 2
14
+ 4,
15
+ // dot 3
16
+ 8,
17
+ // dot 4
18
+ 16,
19
+ // dot 5
20
+ 32,
21
+ // dot 6
22
+ 64,
23
+ // dot 7
24
+ 128
25
+ // dot 8
26
+ ];
9
27
  function normalize(coord) {
10
28
  return Math.round(coord);
11
29
  }
@@ -96,6 +114,60 @@ function setText(canvas, x, y, text) {
96
114
  rowMap.set(col + i, text[i]);
97
115
  }
98
116
  }
117
+ function braille(dots) {
118
+ let mask = 0;
119
+ for (let i = 0; i < 8; i++) {
120
+ if (dots[i]) {
121
+ mask |= DOT_MASKS[i];
122
+ }
123
+ }
124
+ return String.fromCharCode(BRAILLE_OFFSET + mask);
125
+ }
126
+ function brailleDots(...dots) {
127
+ let mask = 0;
128
+ for (const d of dots) {
129
+ if (d < 1 || d > 8) continue;
130
+ mask |= DOT_MASKS[d - 1];
131
+ }
132
+ return String.fromCharCode(BRAILLE_OFFSET + mask);
133
+ }
134
+ function brailleIcon(...chars) {
135
+ if (chars.length === 0 || chars.length > 3) return "";
136
+ const parts = [];
137
+ for (let i = 0; i < chars.length; i++) {
138
+ const dots = chars[i];
139
+ if (!dots) continue;
140
+ let mask = 0;
141
+ for (const d of dots) {
142
+ if (d < 1 || d > 8) continue;
143
+ mask |= DOT_MASKS[d - 1];
144
+ }
145
+ parts.push(String.fromCharCode(BRAILLE_OFFSET + mask));
146
+ }
147
+ return parts.join("");
148
+ }
149
+ function brailleGrid(pixels) {
150
+ let maxWidth = 0;
151
+ for (const row of pixels) {
152
+ if (row.length > maxWidth) maxWidth = row.length;
153
+ }
154
+ const height = Math.min(pixels.length, 4);
155
+ const width = Math.min(maxWidth, 6);
156
+ const charCount = Math.ceil(width / 2);
157
+ const masks = Array.from({ length: charCount }, () => 0);
158
+ for (let y = 0; y < height; y++) {
159
+ const row = pixels[y];
160
+ if (!row) continue;
161
+ for (let x = 0; x < width; x++) {
162
+ if (!row[x]) continue;
163
+ const charIdx = Math.floor(x / 2);
164
+ const mask = PIXEL_MAP[y]?.[x % 2];
165
+ if (mask === void 0) continue;
166
+ masks[charIdx] = (masks[charIdx] ?? 0) | mask;
167
+ }
168
+ }
169
+ return masks.map((m) => String.fromCharCode(BRAILLE_OFFSET + m)).join("");
170
+ }
99
171
  function rows(canvas, bounds) {
100
172
  if (canvas.chars.size === 0) return [];
101
173
  const allRows = [...canvas.chars.keys()];
@@ -325,8 +397,13 @@ function fixedFrame(canvas, delimiter = "\n") {
325
397
  }
326
398
  export {
327
399
  BRAILLE_OFFSET,
400
+ DOT_MASKS,
328
401
  PIXEL_MAP,
329
402
  animate,
403
+ braille,
404
+ brailleDots,
405
+ brailleGrid,
406
+ brailleIcon,
330
407
  clear,
331
408
  createCanvas,
332
409
  createFixedCanvas,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * btui — Braille TUI canvas for terminal drawing with Unicode braille characters.\n *\n * Braille dot layout per character cell:\n * ,___,\n * |1 4|\n * |2 5|\n * |3 6|\n * |7 8|\n * `````\n *\n * Each character maps to a 2x4 pixel grid.\n * Unicode braille characters: U+2800 to U+28FF.\n */\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst BRAILLE_OFFSET = 0x2800;\n\nconst PIXEL_MAP: readonly (readonly [number, number])[] = [\n\t[0x01, 0x08],\n\t[0x02, 0x10],\n\t[0x04, 0x20],\n\t[0x40, 0x80],\n] as const;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A single cell can hold a braille bitmask (number) or a text character (string). */\ntype CellValue = number | string;\n\n/** Sparse row → col → cell storage. */\ninterface CanvasChars {\n\treadonly [row: number]: {readonly [col: number]: CellValue} | undefined;\n}\n\n/** The braille canvas data structure. */\ninterface Canvas {\n\tchars: Map<number, Map<number, CellValue>>;\n\treadonly lineEnding: string;\n}\n\n/** Options for creating a canvas. */\ninterface CreateCanvasOptions {\n\treadonly lineEnding?: string;\n}\n\n/** Bounds for frame/rows rendering. */\ninterface FrameBounds {\n\treadonly minX?: number;\n\treadonly minY?: number;\n\treadonly maxX?: number;\n\treadonly maxY?: number;\n}\n\n/** A 2D point. */\ninterface Point {\n\treadonly x: number;\n\treadonly y: number;\n}\n\n/** Turtle graphics state. */\ninterface TurtleState {\n\treadonly canvas: Canvas;\n\tposX: number;\n\tposY: number;\n\trotation: number;\n\tbrushOn: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Coordinate helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a coordinate to an integer. */\nfunction normalize(coord: number): number {\n\treturn Math.round(coord);\n}\n\n/** Convert pixel (x, y) to cell (col, row). */\nfunction getPos(x: number, y: number): readonly [number, number] {\n\treturn [Math.floor(normalize(x) / 2), Math.floor(normalize(y) / 4)];\n}\n\n// ---------------------------------------------------------------------------\n// Canvas — creation & mutation\n// ---------------------------------------------------------------------------\n\n/** Create a new braille canvas. */\nfunction createCanvas(options?: CreateCanvasOptions): Canvas {\n\treturn {\n\t\tchars: new Map(),\n\t\tlineEnding: options?.lineEnding ?? '\\n',\n\t};\n}\n\n/** Remove all pixels from the canvas. */\nfunction clear(canvas: Canvas): void {\n\tcanvas.chars.clear();\n}\n\n/** Set (draw) a pixel at (x, y). */\nfunction set(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tlet rowMap = canvas.chars.get(row);\n\tif (!rowMap) {\n\t\trowMap = new Map();\n\t\tcanvas.chars.set(row, rowMap);\n\t}\n\n\tconst current = rowMap.get(col);\n\tif (current !== undefined && typeof current !== 'number') {\n\t\treturn;\n\t}\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\trowMap.set(col, ((current as number | undefined) ?? 0) | mask);\n}\n\n/** Unset (erase) a pixel at (x, y). */\nfunction unset(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tif (!rowMap) return;\n\n\tconst current = rowMap.get(col);\n\tif (current === undefined || typeof current !== 'number') return;\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\tconst updated = current & ~mask;\n\tif (updated === 0) {\n\t\trowMap.delete(col);\n\t\tif (rowMap.size === 0) {\n\t\t\tcanvas.chars.delete(row);\n\t\t}\n\t\treturn;\n\t}\n\n\trowMap.set(col, updated);\n}\n\n/** Toggle a pixel at (x, y). */\nfunction toggle(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tconst current = rowMap?.get(col);\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\tif (\n\t\tcurrent !== undefined &&\n\t\t(typeof current !== 'number' || (current & mask) !== 0)\n\t) {\n\t\tunset(canvas, x, y);\n\t} else {\n\t\tset(canvas, x, y);\n\t}\n}\n\n/** Get the state of a pixel at (x, y). Returns true if set. */\nfunction get(canvas: Canvas, x: number, y: number): boolean {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tif (!rowMap) return false;\n\n\tconst cell = rowMap.get(col);\n\tif (cell === undefined) return false;\n\tif (typeof cell !== 'number') return true;\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return false;\n\n\treturn (cell & mask) !== 0;\n}\n\n/** Set text at the given pixel coords. Each character occupies one cell column. */\nfunction setText(canvas: Canvas, x: number, y: number, text: string): void {\n\tconst [col, row] = getPos(x, y);\n\n\tlet rowMap = canvas.chars.get(row);\n\tif (!rowMap) {\n\t\trowMap = new Map();\n\t\tcanvas.chars.set(row, rowMap);\n\t}\n\n\tfor (let i = 0; i < text.length; i++) {\n\t\trowMap.set(col + i, text[i] as string);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Canvas — rendering\n// ---------------------------------------------------------------------------\n\n/** Return the canvas content as an array of strings (one per row). */\nfunction rows(canvas: Canvas, bounds?: FrameBounds): readonly string[] {\n\tif (canvas.chars.size === 0) return [];\n\n\tconst allRows = [...canvas.chars.keys()];\n\tconst minRow =\n\t\tbounds?.minY != null\n\t\t\t? Math.floor(bounds.minY / 4)\n\t\t\t: Math.min(...allRows);\n\tconst maxRow =\n\t\tbounds?.maxY != null\n\t\t\t? Math.floor((bounds.maxY - 1) / 4)\n\t\t\t: Math.max(...allRows);\n\n\t// Global min col for left alignment\n\tlet globalMinCol: number;\n\tif (bounds?.minX != null) {\n\t\tglobalMinCol = Math.floor(bounds.minX / 2);\n\t} else {\n\t\tlet min = Number.POSITIVE_INFINITY;\n\t\tfor (const rowMap of canvas.chars.values()) {\n\t\t\tfor (const c of rowMap.keys()) {\n\t\t\t\tif (c < min) min = c;\n\t\t\t}\n\t\t}\n\t\tglobalMinCol = min === Number.POSITIVE_INFINITY ? 0 : min;\n\t}\n\n\tconst result: string[] = [];\n\n\tfor (let rowNum = minRow; rowNum <= maxRow; rowNum++) {\n\t\tconst rowMap = canvas.chars.get(rowNum);\n\t\tif (!rowMap) {\n\t\t\tresult.push('');\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet maxCol: number;\n\t\tif (bounds?.maxX != null) {\n\t\t\tmaxCol = Math.floor((bounds.maxX - 1) / 2);\n\t\t} else {\n\t\t\tmaxCol = Math.max(...rowMap.keys());\n\t\t}\n\n\t\tconst chars: string[] = [];\n\t\tfor (let c = globalMinCol; c <= maxCol; c++) {\n\t\t\tconst cell = rowMap.get(c);\n\t\t\tif (cell === undefined || cell === 0) {\n\t\t\t\tchars.push(' ');\n\t\t\t} else if (typeof cell !== 'number') {\n\t\t\t\tchars.push(cell);\n\t\t\t} else {\n\t\t\t\tchars.push(String.fromCharCode(BRAILLE_OFFSET + cell));\n\t\t\t}\n\t\t}\n\n\t\tresult.push(chars.join(''));\n\t}\n\n\treturn result;\n}\n\n/** Render the canvas as a string. */\nfunction frame(canvas: Canvas, bounds?: FrameBounds): string {\n\treturn rows(canvas, bounds).join(canvas.lineEnding);\n}\n\n// ---------------------------------------------------------------------------\n// Drawing utilities — line\n// ---------------------------------------------------------------------------\n\n/** Generate coordinates along a line from (x1, y1) to (x2, y2) using Bresenham-style interpolation. */\nfunction* line(\n\tx1: number,\n\ty1: number,\n\tx2: number,\n\ty2: number,\n): Generator<Point, void, unknown> {\n\tconst nx1 = normalize(x1);\n\tconst ny1 = normalize(y1);\n\tconst nx2 = normalize(x2);\n\tconst ny2 = normalize(y2);\n\n\tconst xDiff = Math.abs(nx2 - nx1);\n\tconst yDiff = Math.abs(ny2 - ny1);\n\tconst xDir = nx1 <= nx2 ? 1 : -1;\n\tconst yDir = ny1 <= ny2 ? 1 : -1;\n\n\tconst steps = Math.max(xDiff, yDiff);\n\n\tfor (let i = 0; i <= steps; i++) {\n\t\tlet px = nx1;\n\t\tlet py = ny1;\n\n\t\tif (yDiff) {\n\t\t\tpy += Math.round((i * yDiff) / steps) * yDir;\n\t\t}\n\t\tif (xDiff) {\n\t\t\tpx += Math.round((i * xDiff) / steps) * xDir;\n\t\t}\n\n\t\tyield {x: px, y: py};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Drawing utilities — polygon\n// ---------------------------------------------------------------------------\n\n/** Generate coordinates for a regular polygon. */\nfunction* polygon(\n\tcenterX = 0,\n\tcenterY = 0,\n\tsides = 4,\n\tradius = 4,\n): Generator<Point, void, unknown> {\n\tconst degree = 360 / sides;\n\n\tfor (let n = 0; n < sides; n++) {\n\t\tconst a = n * degree;\n\t\tconst b = (n + 1) * degree;\n\t\tconst x1 =\n\t\t\t(centerX + Math.cos((a * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst y1 =\n\t\t\t(centerY + Math.sin((a * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst x2 =\n\t\t\t(centerX + Math.cos((b * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst y2 =\n\t\t\t(centerY + Math.sin((b * Math.PI) / 180)) * ((radius + 1) / 2);\n\n\t\tfor (const pt of line(x1, y1, x2, y2)) {\n\t\t\tyield pt;\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Turtle graphics\n// ---------------------------------------------------------------------------\n\n/** Create a new turtle state. */\nfunction createTurtle(posX = 0, posY = 0): TurtleState {\n\treturn {\n\t\tcanvas: createCanvas(),\n\t\tposX,\n\t\tposY,\n\t\trotation: 0,\n\t\tbrushOn: true,\n\t};\n}\n\n/** Pull the brush up (stop drawing on move). */\nfunction turtleUp(t: TurtleState): void {\n\tt.brushOn = false;\n}\n\n/** Push the brush down (draw on move). */\nfunction turtleDown(t: TurtleState): void {\n\tt.brushOn = true;\n}\n\n/** Move the turtle to an absolute position, drawing if the brush is down. */\nfunction turtleMove(t: TurtleState, x: number, y: number): void {\n\tif (t.brushOn) {\n\t\tfor (const pt of line(t.posX, t.posY, x, y)) {\n\t\t\tset(t.canvas, pt.x, pt.y);\n\t\t}\n\t}\n\tt.posX = x;\n\tt.posY = y;\n}\n\n/** Move the turtle forward by `step` pixels in its current direction. */\nfunction turtleForward(t: TurtleState, step: number): void {\n\tconst x = t.posX + Math.cos((t.rotation * Math.PI) / 180) * step;\n\tconst y = t.posY + Math.sin((t.rotation * Math.PI) / 180) * step;\n\tconst prevBrush = t.brushOn;\n\tt.brushOn = true;\n\tturtleMove(t, x, y);\n\tt.brushOn = prevBrush;\n}\n\n/** Move the turtle backward by `step` pixels. */\nfunction turtleBack(t: TurtleState, step: number): void {\n\tturtleForward(t, -step);\n}\n\n/** Rotate the turtle right (clockwise) by `angle` degrees. */\nfunction turtleRight(t: TurtleState, angle: number): void {\n\tt.rotation += angle;\n}\n\n/** Rotate the turtle left (counter-clockwise) by `angle` degrees. */\nfunction turtleLeft(t: TurtleState, angle: number): void {\n\tt.rotation -= angle;\n}\n\n// ---------------------------------------------------------------------------\n// Terminal size\n// ---------------------------------------------------------------------------\n\n/** Terminal dimensions in characters. */\ninterface TerminalSize {\n\treadonly width: number;\n\treadonly height: number;\n}\n\n/** Get terminal width and height. Falls back to 80x25. */\nfunction getTerminalSize(): TerminalSize {\n\tconst width = process.stdout?.columns ?? 80;\n\tconst height = process.stdout?.rows ?? 25;\n\treturn {width, height};\n}\n\n// ---------------------------------------------------------------------------\n// Animation\n// ---------------------------------------------------------------------------\n\n/** Options for the animate function. */\ninterface AnimateOptions {\n\treadonly delay?: number;\n\treadonly signal?: AbortSignal;\n}\n\n/** Frame generator yields arrays of points per frame. */\ntype FrameGenerator = Generator<readonly Point[], void, unknown>;\n\n/** Sleep for the given number of milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/** Run an animation loop. Clears the canvas each frame, draws points from the generator, and writes to stdout. */\nasync function animate(\n\tcanvas: Canvas,\n\tfn: () => FrameGenerator,\n\toptions?: AnimateOptions,\n): Promise<void> {\n\tconst delay = options?.delay ?? 1000 / 24;\n\n\tfor (const points of fn()) {\n\t\tif (options?.signal?.aborted) break;\n\n\t\tfor (const pt of points) {\n\t\t\tset(canvas, pt.x, pt.y);\n\t\t}\n\n\t\tconst f = frame(canvas);\n\t\tprocess.stdout.write(`\\x1b[H\\x1b[J${f}\\n`);\n\n\t\tif (delay > 0) {\n\t\t\tawait sleep(delay);\n\t\t}\n\n\t\tclear(canvas);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Fixed-size canvas (node-drawille style)\n// ---------------------------------------------------------------------------\n\n/** A fixed-size canvas backed by a Uint8Array buffer. */\ninterface FixedCanvas {\n\treadonly width: number;\n\treadonly height: number;\n\treadonly content: Uint8Array;\n}\n\n/** Create a fixed-size canvas with the given dimensions. Width rounds to multiple of 2, height to multiple of 4. */\nfunction createFixedCanvas(width: number, height: number): FixedCanvas {\n\tconst w = Math.floor(width / 2) * 2;\n\tconst h = Math.floor(height / 4) * 4;\n\treturn {\n\t\twidth: w,\n\t\theight: h,\n\t\tcontent: new Uint8Array((w * h) / 8),\n\t};\n}\n\n/** Clear all pixels from a fixed canvas. */\nfunction fixedClear(canvas: FixedCanvas): void {\n\tcanvas.content.fill(0);\n}\n\n/** Set a pixel on a fixed canvas. */\nfunction fixedSet(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val | mask;\n}\n\n/** Unset a pixel on a fixed canvas. */\nfunction fixedUnset(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val & ~mask;\n}\n\n/** Toggle a pixel on a fixed canvas. */\nfunction fixedToggle(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val ^ mask;\n}\n\n/** Get the state of a pixel on a fixed canvas. */\nfunction fixedGet(canvas: FixedCanvas, x: number, y: number): boolean {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height))\n\t\treturn false;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return false;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return false;\n\treturn (val & mask) !== 0;\n}\n\n/** Render a fixed canvas as a string. */\nfunction fixedFrame(canvas: FixedCanvas, delimiter = '\\n'): string {\n\tconst frameWidth = canvas.width / 2;\n\tconst parts: string[] = [];\n\n\tfor (let i = 0; i < canvas.content.length; i++) {\n\t\tif (i % frameWidth === 0) {\n\t\t\tparts.push(delimiter);\n\t\t}\n\t\tconst val = canvas.content[i];\n\t\tparts.push(val ? String.fromCharCode(BRAILLE_OFFSET + val) : ' ');\n\t}\n\tparts.push(delimiter);\n\n\treturn parts.join('');\n}\n\n// ---------------------------------------------------------------------------\n// Exports\n// ---------------------------------------------------------------------------\n\nexport {\n\t// Constants\n\tBRAILLE_OFFSET,\n\tPIXEL_MAP,\n\t// Types\n\ttype AnimateOptions,\n\ttype Canvas,\n\ttype CanvasChars,\n\ttype CellValue,\n\ttype CreateCanvasOptions,\n\ttype FixedCanvas,\n\ttype FrameBounds,\n\ttype FrameGenerator,\n\ttype Point,\n\ttype TerminalSize,\n\ttype TurtleState,\n\t// Coordinate helpers\n\tnormalize,\n\tgetPos,\n\t// Terminal\n\tgetTerminalSize,\n\t// Sparse canvas\n\tcreateCanvas,\n\tclear,\n\tset,\n\tunset,\n\ttoggle,\n\tget,\n\tsetText,\n\trows,\n\tframe,\n\t// Animation\n\tanimate,\n\t// Drawing utilities\n\tline,\n\tpolygon,\n\t// Turtle\n\tcreateTurtle,\n\tturtleUp,\n\tturtleDown,\n\tturtleMove,\n\tturtleForward,\n\tturtleBack,\n\tturtleRight,\n\tturtleLeft,\n\t// Fixed canvas\n\tcreateFixedCanvas,\n\tfixedClear,\n\tfixedSet,\n\tfixedUnset,\n\tfixedToggle,\n\tfixedGet,\n\tfixedFrame,\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * btui — Braille TUI canvas for terminal drawing with Unicode braille characters.\n *\n * Braille dot layout per character cell:\n * ,___,\n * |1 4|\n * |2 5|\n * |3 6|\n * |7 8|\n * `````\n *\n * Each character maps to a 2x4 pixel grid.\n * Unicode braille characters: U+2800 to U+28FF.\n */\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst BRAILLE_OFFSET = 0x2800;\n\nconst PIXEL_MAP: readonly (readonly [number, number])[] = [\n\t[0x01, 0x08],\n\t[0x02, 0x10],\n\t[0x04, 0x20],\n\t[0x40, 0x80],\n] as const;\n\n/** Bitmask for each braille dot number (1–8). */\nconst DOT_MASKS: readonly number[] = [\n\t0x01, // dot 1\n\t0x02, // dot 2\n\t0x04, // dot 3\n\t0x08, // dot 4\n\t0x10, // dot 5\n\t0x20, // dot 6\n\t0x40, // dot 7\n\t0x80, // dot 8\n] as const;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A single cell can hold a braille bitmask (number) or a text character (string). */\ntype CellValue = number | string;\n\n/** Sparse row → col → cell storage. */\ninterface CanvasChars {\n\treadonly [row: number]: {readonly [col: number]: CellValue} | undefined;\n}\n\n/** The braille canvas data structure. */\ninterface Canvas {\n\tchars: Map<number, Map<number, CellValue>>;\n\treadonly lineEnding: string;\n}\n\n/** Options for creating a canvas. */\ninterface CreateCanvasOptions {\n\treadonly lineEnding?: string;\n}\n\n/** Bounds for frame/rows rendering. */\ninterface FrameBounds {\n\treadonly minX?: number;\n\treadonly minY?: number;\n\treadonly maxX?: number;\n\treadonly maxY?: number;\n}\n\n/** A 2D point. */\ninterface Point {\n\treadonly x: number;\n\treadonly y: number;\n}\n\n/** Turtle graphics state. */\ninterface TurtleState {\n\treadonly canvas: Canvas;\n\tposX: number;\n\tposY: number;\n\trotation: number;\n\tbrushOn: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Coordinate helpers\n// ---------------------------------------------------------------------------\n\n/** Normalize a coordinate to an integer. */\nfunction normalize(coord: number): number {\n\treturn Math.round(coord);\n}\n\n/** Convert pixel (x, y) to cell (col, row). */\nfunction getPos(x: number, y: number): readonly [number, number] {\n\treturn [Math.floor(normalize(x) / 2), Math.floor(normalize(y) / 4)];\n}\n\n// ---------------------------------------------------------------------------\n// Canvas — creation & mutation\n// ---------------------------------------------------------------------------\n\n/** Create a new braille canvas. */\nfunction createCanvas(options?: CreateCanvasOptions): Canvas {\n\treturn {\n\t\tchars: new Map(),\n\t\tlineEnding: options?.lineEnding ?? '\\n',\n\t};\n}\n\n/** Remove all pixels from the canvas. */\nfunction clear(canvas: Canvas): void {\n\tcanvas.chars.clear();\n}\n\n/** Set (draw) a pixel at (x, y). */\nfunction set(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tlet rowMap = canvas.chars.get(row);\n\tif (!rowMap) {\n\t\trowMap = new Map();\n\t\tcanvas.chars.set(row, rowMap);\n\t}\n\n\tconst current = rowMap.get(col);\n\tif (current !== undefined && typeof current !== 'number') {\n\t\treturn;\n\t}\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\trowMap.set(col, ((current as number | undefined) ?? 0) | mask);\n}\n\n/** Unset (erase) a pixel at (x, y). */\nfunction unset(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tif (!rowMap) return;\n\n\tconst current = rowMap.get(col);\n\tif (current === undefined || typeof current !== 'number') return;\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\tconst updated = current & ~mask;\n\tif (updated === 0) {\n\t\trowMap.delete(col);\n\t\tif (rowMap.size === 0) {\n\t\t\tcanvas.chars.delete(row);\n\t\t}\n\t\treturn;\n\t}\n\n\trowMap.set(col, updated);\n}\n\n/** Toggle a pixel at (x, y). */\nfunction toggle(canvas: Canvas, x: number, y: number): void {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tconst current = rowMap?.get(col);\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return;\n\n\tif (\n\t\tcurrent !== undefined &&\n\t\t(typeof current !== 'number' || (current & mask) !== 0)\n\t) {\n\t\tunset(canvas, x, y);\n\t} else {\n\t\tset(canvas, x, y);\n\t}\n}\n\n/** Get the state of a pixel at (x, y). Returns true if set. */\nfunction get(canvas: Canvas, x: number, y: number): boolean {\n\tconst nx = normalize(x);\n\tconst ny = normalize(y);\n\tconst [col, row] = getPos(nx, ny);\n\n\tconst rowMap = canvas.chars.get(row);\n\tif (!rowMap) return false;\n\n\tconst cell = rowMap.get(col);\n\tif (cell === undefined) return false;\n\tif (typeof cell !== 'number') return true;\n\n\tconst mask = PIXEL_MAP[ny % 4]?.[nx % 2];\n\tif (mask === undefined) return false;\n\n\treturn (cell & mask) !== 0;\n}\n\n/** Set text at the given pixel coords. Each character occupies one cell column. */\nfunction setText(canvas: Canvas, x: number, y: number, text: string): void {\n\tconst [col, row] = getPos(x, y);\n\n\tlet rowMap = canvas.chars.get(row);\n\tif (!rowMap) {\n\t\trowMap = new Map();\n\t\tcanvas.chars.set(row, rowMap);\n\t}\n\n\tfor (let i = 0; i < text.length; i++) {\n\t\trowMap.set(col + i, text[i] as string);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Braille character builders\n// ---------------------------------------------------------------------------\n\n/**\n * 8-element tuple: on/off state for dots 1–8.\n *\n * ,___,\n * |1 4|\n * |2 5|\n * |3 6|\n * |7 8|\n * `````\n */\ntype DotPattern = readonly [\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n\tboolean,\n];\n\n/** Build a braille character from an 8-element on/off pattern (dots 1–8). */\nfunction braille(dots: DotPattern): string {\n\tlet mask = 0;\n\tfor (let i = 0; i < 8; i++) {\n\t\tif (dots[i]) {\n\t\t\tmask |= DOT_MASKS[i] as number;\n\t\t}\n\t}\n\treturn String.fromCharCode(BRAILLE_OFFSET + mask);\n}\n\n/** Build a braille character by listing which dot numbers (1–8) are on. */\nfunction brailleDots(...dots: readonly number[]): string {\n\tlet mask = 0;\n\tfor (const d of dots) {\n\t\tif (d < 1 || d > 8) continue;\n\t\tmask |= DOT_MASKS[d - 1] as number;\n\t}\n\treturn String.fromCharCode(BRAILLE_OFFSET + mask);\n}\n\n/**\n * Build a 1–3 character braille icon. Each argument is an array of\n * dot numbers (1–8) that are ON for that character position.\n *\n * @example\n * brailleIcon([1, 2, 3, 7], [4, 5, 6, 8]) // \"⡇⢸\" — left bar + right bar\n * brailleIcon([1, 4]) // \"⠉\" — single char, top row\n * brailleIcon([1], [1, 8], [1]) // 3-char\n */\nfunction brailleIcon(...chars: readonly (readonly number[])[]): string {\n\tif (chars.length === 0 || chars.length > 3) return '';\n\n\tconst parts: string[] = [];\n\tfor (let i = 0; i < chars.length; i++) {\n\t\tconst dots = chars[i];\n\t\tif (!dots) continue;\n\t\tlet mask = 0;\n\t\tfor (const d of dots) {\n\t\t\tif (d < 1 || d > 8) continue;\n\t\t\tmask |= DOT_MASKS[d - 1] as number;\n\t\t}\n\t\tparts.push(String.fromCharCode(BRAILLE_OFFSET + mask));\n\t}\n\treturn parts.join('');\n}\n\n/**\n * Build a braille icon from a visual pixel grid.\n * Each row is an array of 0/1 values. Max 6 columns wide × 4 rows tall.\n * Every 2 columns map to one braille character.\n *\n * col: 0 1 │ 2 3 │ 4 5\n * ────┼─────┼────\n * row0: 1 4 │ 1 4 │ 1 4\n * row1: 2 5 │ 2 5 │ 2 5\n * row2: 3 6 │ 3 6 │ 3 6\n * row3: 7 8 │ 7 8 │ 7 8\n * ─ch0─ ─ch1─ ─ch2─\n *\n * @example\n * brailleGrid([\n * [0, 1, 1, 0],\n * [1, 0, 0, 1],\n * [1, 0, 0, 1],\n * [0, 1, 1, 0],\n * ]) // \"⠳⠞\" — a diamond shape, 2 chars wide\n */\nfunction brailleGrid(pixels: readonly (readonly number[])[]): string {\n\tlet maxWidth = 0;\n\tfor (const row of pixels) {\n\t\tif (row.length > maxWidth) maxWidth = row.length;\n\t}\n\n\tconst height = Math.min(pixels.length, 4);\n\tconst width = Math.min(maxWidth, 6);\n\tconst charCount = Math.ceil(width / 2);\n\n\tconst masks: number[] = Array.from({length: charCount}, () => 0);\n\n\tfor (let y = 0; y < height; y++) {\n\t\tconst row = pixels[y];\n\t\tif (!row) continue;\n\t\tfor (let x = 0; x < width; x++) {\n\t\t\tif (!row[x]) continue;\n\t\t\tconst charIdx = Math.floor(x / 2);\n\t\t\tconst mask = PIXEL_MAP[y]?.[x % 2];\n\t\t\tif (mask === undefined) continue;\n\t\t\tmasks[charIdx] = (masks[charIdx] ?? 0) | mask;\n\t\t}\n\t}\n\n\treturn masks.map(m => String.fromCharCode(BRAILLE_OFFSET + m)).join('');\n}\n\n// ---------------------------------------------------------------------------\n// Canvas — rendering\n// ---------------------------------------------------------------------------\n\n/** Return the canvas content as an array of strings (one per row). */\nfunction rows(canvas: Canvas, bounds?: FrameBounds): readonly string[] {\n\tif (canvas.chars.size === 0) return [];\n\n\tconst allRows = [...canvas.chars.keys()];\n\tconst minRow =\n\t\tbounds?.minY != null\n\t\t\t? Math.floor(bounds.minY / 4)\n\t\t\t: Math.min(...allRows);\n\tconst maxRow =\n\t\tbounds?.maxY != null\n\t\t\t? Math.floor((bounds.maxY - 1) / 4)\n\t\t\t: Math.max(...allRows);\n\n\t// Global min col for left alignment\n\tlet globalMinCol: number;\n\tif (bounds?.minX != null) {\n\t\tglobalMinCol = Math.floor(bounds.minX / 2);\n\t} else {\n\t\tlet min = Number.POSITIVE_INFINITY;\n\t\tfor (const rowMap of canvas.chars.values()) {\n\t\t\tfor (const c of rowMap.keys()) {\n\t\t\t\tif (c < min) min = c;\n\t\t\t}\n\t\t}\n\t\tglobalMinCol = min === Number.POSITIVE_INFINITY ? 0 : min;\n\t}\n\n\tconst result: string[] = [];\n\n\tfor (let rowNum = minRow; rowNum <= maxRow; rowNum++) {\n\t\tconst rowMap = canvas.chars.get(rowNum);\n\t\tif (!rowMap) {\n\t\t\tresult.push('');\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet maxCol: number;\n\t\tif (bounds?.maxX != null) {\n\t\t\tmaxCol = Math.floor((bounds.maxX - 1) / 2);\n\t\t} else {\n\t\t\tmaxCol = Math.max(...rowMap.keys());\n\t\t}\n\n\t\tconst chars: string[] = [];\n\t\tfor (let c = globalMinCol; c <= maxCol; c++) {\n\t\t\tconst cell = rowMap.get(c);\n\t\t\tif (cell === undefined || cell === 0) {\n\t\t\t\tchars.push(' ');\n\t\t\t} else if (typeof cell !== 'number') {\n\t\t\t\tchars.push(cell);\n\t\t\t} else {\n\t\t\t\tchars.push(String.fromCharCode(BRAILLE_OFFSET + cell));\n\t\t\t}\n\t\t}\n\n\t\tresult.push(chars.join(''));\n\t}\n\n\treturn result;\n}\n\n/** Render the canvas as a string. */\nfunction frame(canvas: Canvas, bounds?: FrameBounds): string {\n\treturn rows(canvas, bounds).join(canvas.lineEnding);\n}\n\n// ---------------------------------------------------------------------------\n// Drawing utilities — line\n// ---------------------------------------------------------------------------\n\n/** Generate coordinates along a line from (x1, y1) to (x2, y2) using Bresenham-style interpolation. */\nfunction* line(\n\tx1: number,\n\ty1: number,\n\tx2: number,\n\ty2: number,\n): Generator<Point, void, unknown> {\n\tconst nx1 = normalize(x1);\n\tconst ny1 = normalize(y1);\n\tconst nx2 = normalize(x2);\n\tconst ny2 = normalize(y2);\n\n\tconst xDiff = Math.abs(nx2 - nx1);\n\tconst yDiff = Math.abs(ny2 - ny1);\n\tconst xDir = nx1 <= nx2 ? 1 : -1;\n\tconst yDir = ny1 <= ny2 ? 1 : -1;\n\n\tconst steps = Math.max(xDiff, yDiff);\n\n\tfor (let i = 0; i <= steps; i++) {\n\t\tlet px = nx1;\n\t\tlet py = ny1;\n\n\t\tif (yDiff) {\n\t\t\tpy += Math.round((i * yDiff) / steps) * yDir;\n\t\t}\n\t\tif (xDiff) {\n\t\t\tpx += Math.round((i * xDiff) / steps) * xDir;\n\t\t}\n\n\t\tyield {x: px, y: py};\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Drawing utilities — polygon\n// ---------------------------------------------------------------------------\n\n/** Generate coordinates for a regular polygon. */\nfunction* polygon(\n\tcenterX = 0,\n\tcenterY = 0,\n\tsides = 4,\n\tradius = 4,\n): Generator<Point, void, unknown> {\n\tconst degree = 360 / sides;\n\n\tfor (let n = 0; n < sides; n++) {\n\t\tconst a = n * degree;\n\t\tconst b = (n + 1) * degree;\n\t\tconst x1 =\n\t\t\t(centerX + Math.cos((a * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst y1 =\n\t\t\t(centerY + Math.sin((a * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst x2 =\n\t\t\t(centerX + Math.cos((b * Math.PI) / 180)) * ((radius + 1) / 2);\n\t\tconst y2 =\n\t\t\t(centerY + Math.sin((b * Math.PI) / 180)) * ((radius + 1) / 2);\n\n\t\tfor (const pt of line(x1, y1, x2, y2)) {\n\t\t\tyield pt;\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Turtle graphics\n// ---------------------------------------------------------------------------\n\n/** Create a new turtle state. */\nfunction createTurtle(posX = 0, posY = 0): TurtleState {\n\treturn {\n\t\tcanvas: createCanvas(),\n\t\tposX,\n\t\tposY,\n\t\trotation: 0,\n\t\tbrushOn: true,\n\t};\n}\n\n/** Pull the brush up (stop drawing on move). */\nfunction turtleUp(t: TurtleState): void {\n\tt.brushOn = false;\n}\n\n/** Push the brush down (draw on move). */\nfunction turtleDown(t: TurtleState): void {\n\tt.brushOn = true;\n}\n\n/** Move the turtle to an absolute position, drawing if the brush is down. */\nfunction turtleMove(t: TurtleState, x: number, y: number): void {\n\tif (t.brushOn) {\n\t\tfor (const pt of line(t.posX, t.posY, x, y)) {\n\t\t\tset(t.canvas, pt.x, pt.y);\n\t\t}\n\t}\n\tt.posX = x;\n\tt.posY = y;\n}\n\n/** Move the turtle forward by `step` pixels in its current direction. */\nfunction turtleForward(t: TurtleState, step: number): void {\n\tconst x = t.posX + Math.cos((t.rotation * Math.PI) / 180) * step;\n\tconst y = t.posY + Math.sin((t.rotation * Math.PI) / 180) * step;\n\tconst prevBrush = t.brushOn;\n\tt.brushOn = true;\n\tturtleMove(t, x, y);\n\tt.brushOn = prevBrush;\n}\n\n/** Move the turtle backward by `step` pixels. */\nfunction turtleBack(t: TurtleState, step: number): void {\n\tturtleForward(t, -step);\n}\n\n/** Rotate the turtle right (clockwise) by `angle` degrees. */\nfunction turtleRight(t: TurtleState, angle: number): void {\n\tt.rotation += angle;\n}\n\n/** Rotate the turtle left (counter-clockwise) by `angle` degrees. */\nfunction turtleLeft(t: TurtleState, angle: number): void {\n\tt.rotation -= angle;\n}\n\n// ---------------------------------------------------------------------------\n// Terminal size\n// ---------------------------------------------------------------------------\n\n/** Terminal dimensions in characters. */\ninterface TerminalSize {\n\treadonly width: number;\n\treadonly height: number;\n}\n\n/** Get terminal width and height. Falls back to 80x25. */\nfunction getTerminalSize(): TerminalSize {\n\tconst width = process.stdout?.columns ?? 80;\n\tconst height = process.stdout?.rows ?? 25;\n\treturn {width, height};\n}\n\n// ---------------------------------------------------------------------------\n// Animation\n// ---------------------------------------------------------------------------\n\n/** Options for the animate function. */\ninterface AnimateOptions {\n\treadonly delay?: number;\n\treadonly signal?: AbortSignal;\n}\n\n/** Frame generator yields arrays of points per frame. */\ntype FrameGenerator = Generator<readonly Point[], void, unknown>;\n\n/** Sleep for the given number of milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/** Run an animation loop. Clears the canvas each frame, draws points from the generator, and writes to stdout. */\nasync function animate(\n\tcanvas: Canvas,\n\tfn: () => FrameGenerator,\n\toptions?: AnimateOptions,\n): Promise<void> {\n\tconst delay = options?.delay ?? 1000 / 24;\n\n\tfor (const points of fn()) {\n\t\tif (options?.signal?.aborted) break;\n\n\t\tfor (const pt of points) {\n\t\t\tset(canvas, pt.x, pt.y);\n\t\t}\n\n\t\tconst f = frame(canvas);\n\t\tprocess.stdout.write(`\\x1b[H\\x1b[J${f}\\n`);\n\n\t\tif (delay > 0) {\n\t\t\tawait sleep(delay);\n\t\t}\n\n\t\tclear(canvas);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Fixed-size canvas (node-drawille style)\n// ---------------------------------------------------------------------------\n\n/** A fixed-size canvas backed by a Uint8Array buffer. */\ninterface FixedCanvas {\n\treadonly width: number;\n\treadonly height: number;\n\treadonly content: Uint8Array;\n}\n\n/** Create a fixed-size canvas with the given dimensions. Width rounds to multiple of 2, height to multiple of 4. */\nfunction createFixedCanvas(width: number, height: number): FixedCanvas {\n\tconst w = Math.floor(width / 2) * 2;\n\tconst h = Math.floor(height / 4) * 4;\n\treturn {\n\t\twidth: w,\n\t\theight: h,\n\t\tcontent: new Uint8Array((w * h) / 8),\n\t};\n}\n\n/** Clear all pixels from a fixed canvas. */\nfunction fixedClear(canvas: FixedCanvas): void {\n\tcanvas.content.fill(0);\n}\n\n/** Set a pixel on a fixed canvas. */\nfunction fixedSet(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val | mask;\n}\n\n/** Unset a pixel on a fixed canvas. */\nfunction fixedUnset(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val & ~mask;\n}\n\n/** Toggle a pixel on a fixed canvas. */\nfunction fixedToggle(canvas: FixedCanvas, x: number, y: number): void {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height)) return;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return;\n\tcanvas.content[coord] = val ^ mask;\n}\n\n/** Get the state of a pixel on a fixed canvas. */\nfunction fixedGet(canvas: FixedCanvas, x: number, y: number): boolean {\n\tif (!(x >= 0 && x < canvas.width && y >= 0 && y < canvas.height))\n\t\treturn false;\n\tconst fx = Math.floor(x);\n\tconst fy = Math.floor(y);\n\tconst nx = Math.floor(fx / 2);\n\tconst ny = Math.floor(fy / 4);\n\tconst coord = nx + (canvas.width / 2) * ny;\n\tconst mask = PIXEL_MAP[fy % 4]?.[fx % 2];\n\tif (mask === undefined) return false;\n\tconst val = canvas.content[coord];\n\tif (val === undefined) return false;\n\treturn (val & mask) !== 0;\n}\n\n/** Render a fixed canvas as a string. */\nfunction fixedFrame(canvas: FixedCanvas, delimiter = '\\n'): string {\n\tconst frameWidth = canvas.width / 2;\n\tconst parts: string[] = [];\n\n\tfor (let i = 0; i < canvas.content.length; i++) {\n\t\tif (i % frameWidth === 0) {\n\t\t\tparts.push(delimiter);\n\t\t}\n\t\tconst val = canvas.content[i];\n\t\tparts.push(val ? String.fromCharCode(BRAILLE_OFFSET + val) : ' ');\n\t}\n\tparts.push(delimiter);\n\n\treturn parts.join('');\n}\n\n// ---------------------------------------------------------------------------\n// Exports\n// ---------------------------------------------------------------------------\n\nexport {\n\t// Constants\n\tBRAILLE_OFFSET,\n\tPIXEL_MAP,\n\tDOT_MASKS,\n\t// Types\n\ttype AnimateOptions,\n\ttype Canvas,\n\ttype CanvasChars,\n\ttype CellValue,\n\ttype CreateCanvasOptions,\n\ttype DotPattern,\n\ttype FixedCanvas,\n\ttype FrameBounds,\n\ttype FrameGenerator,\n\ttype Point,\n\ttype TerminalSize,\n\ttype TurtleState,\n\t// Coordinate helpers\n\tnormalize,\n\tgetPos,\n\t// Terminal\n\tgetTerminalSize,\n\t// Braille builders\n\tbraille,\n\tbrailleDots,\n\tbrailleIcon,\n\tbrailleGrid,\n\t// Sparse canvas\n\tcreateCanvas,\n\tclear,\n\tset,\n\tunset,\n\ttoggle,\n\tget,\n\tsetText,\n\trows,\n\tframe,\n\t// Animation\n\tanimate,\n\t// Drawing utilities\n\tline,\n\tpolygon,\n\t// Turtle\n\tcreateTurtle,\n\tturtleUp,\n\tturtleDown,\n\tturtleMove,\n\tturtleForward,\n\tturtleBack,\n\tturtleRight,\n\tturtleLeft,\n\t// Fixed canvas\n\tcreateFixedCanvas,\n\tfixedClear,\n\tfixedSet,\n\tfixedUnset,\n\tfixedToggle,\n\tfixedGet,\n\tfixedFrame,\n};\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termdot",
3
- "version": "0.0.1",
3
+ "version": "0.1.5",
4
4
  "description": "Braille TUI — draw in the terminal with Unicode braille characters",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -13,14 +13,17 @@
13
13
  "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
- "files": ["dist"],
16
+ "files": [
17
+ "dist"
18
+ ],
17
19
  "scripts": {
18
20
  "build": "tsup",
19
21
  "test": "vitest run",
20
22
  "test:watch": "vitest",
21
23
  "lint": "biome check .",
22
24
  "lint:fix": "biome check --fix .",
23
- "typecheck": "tsc --noEmit"
25
+ "typecheck": "tsc --noEmit",
26
+ "changelog": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s"
24
27
  },
25
28
  "keywords": [
26
29
  "braille",
@@ -35,6 +38,7 @@
35
38
  "devDependencies": {
36
39
  "@biomejs/biome": "^1.9.4",
37
40
  "@types/node": "^25.2.3",
41
+ "conventional-changelog-cli": "^5.0.0",
38
42
  "tsup": "^8.4.0",
39
43
  "typescript": "^5.7.3",
40
44
  "vitest": "^3.0.5"