{"_id":"@bomb.sh/tty","_rev":"4-94aa132774f1f4bb9a6bc21b233bfd4a","name":"@bomb.sh/tty","dist-tags":{"latest":"0.9.0"},"versions":{"0.0.0-register.0":{"name":"@bomb.sh/tty","version":"0.0.0-register.0","license":"MIT","_id":"@bomb.sh/tty@0.0.0-register.0","maintainers":[{"name":"natemoo-re","email":"nate@natemoo.re"},{"name":"jbolda","email":"me@jacobbolda.com"},{"name":"cowboyd","email":"cowboyd@frontside.com"},{"name":"amirsa12","email":"amirhosseinpr184@gmail.com"},{"name":"dreyfus92","email":"paulvi92@gmail.com"},{"name":"aslemammad","email":"zorofight94@gmail.com"},{"name":"43081j","email":"43081james@gmail.com"}],"homepage":"https://github.com/bombshell-dev/tty#readme","bugs":{"url":"https://github.com/bombshell-dev/tty/issues"},"dist":{"shasum":"273d82d5631303bcd5dbd41ba0ac15ffc3199d59","tarball":"https://registry.npmjs.org/@bomb.sh/tty/-/tty-0.0.0-register.0.tgz","fileCount":23,"integrity":"sha512-2XWSB0IcTxwa26KjS2v/AB4CJLa7g5iuElh8X811Ms6GzcYNLrhgW9m9jyZTrH0gaNivOPiT9eCcqqq3Dn2F4Q==","signatures":[{"sig":"MEUCIQDZjih+qtZsZpZH9C8ri0R8VZKMFXl1S6I7Yto0WlUTeAIgcribRsFJBueEsAgtU1xhHRvGCksH/oTqSTZlany1284=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121697},"type":"module","module":"./esm/mod.js","engines":{"node":">= 22"},"exports":{".":{"import":"./esm/mod.js"}},"gitHead":"f986009e6745ced97587c0c513f5175c5f8b18b1","scripts":{},"_npmUser":{"name":"cowboyd","email":"cowboyd@frontside.com"},"repository":{"url":"git+https://github.com/bombshell-dev/tty.git","type":"git"},"_npmVersion":"11.12.1","description":"A terminal rendering backend for Clay, compiled to WebAssembly","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/tty_0.0.0-register.0_1781814298374_0.14557049507172581","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@bomb.sh/tty","version":"0.7.0","license":"MIT","_id":"@bomb.sh/tty@0.7.0","maintainers":[{"name":"natemoo-re","email":"nate@natemoo.re"},{"name":"jbolda","email":"me@jacobbolda.com"},{"name":"cowboyd","email":"cowboyd@frontside.com"},{"name":"amirsa12","email":"amirhosseinpr184@gmail.com"},{"name":"dreyfus92","email":"paulvi92@gmail.com"},{"name":"aslemammad","email":"zorofight94@gmail.com"},{"name":"43081j","email":"43081james@gmail.com"}],"homepage":"https://github.com/bombshell-dev/tty#readme","bugs":{"url":"https://github.com/bombshell-dev/tty/issues"},"dist":{"shasum":"06501e40dba9a5245a2f6570ebc51e739ac9c960","tarball":"https://registry.npmjs.org/@bomb.sh/tty/-/tty-0.7.0.tgz","fileCount":23,"integrity":"sha512-htTavc3o84vR6uSoyF+Y9u81GG8NoTJkwOovahcNOY4nVeiM86vodCHdPvAXoj2ucmx04frpwMrOJ4ipLSSJfw==","signatures":[{"sig":"MEUCICVpM3C82BLhOZHrem1f8jp2PrLWGTS3otlkJGuzBQo4AiEA0clWstjivGZKPjYWciLbbe1gDBzhVt/vD0UJ6YTGBEw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bomb.sh%2ftty@0.7.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":120626},"type":"module","module":"./esm/mod.js","engines":{"node":">= 22"},"exports":{".":{"import":"./esm/mod.js"}},"scripts":{},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f10e0bb9-51fa-4201-95d9-c578cd8f200a"}},"repository":{"url":"git+https://github.com/bombshell-dev/tty.git","type":"git"},"_npmVersion":"11.13.0","description":"A terminal rendering backend for Clay, compiled to WebAssembly","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/tty_0.7.0_1781814801239_0.009259583417468242","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"@bomb.sh/tty","version":"0.8.0","license":"MIT","_id":"@bomb.sh/tty@0.8.0","maintainers":[{"name":"natemoo-re","email":"nate@natemoo.re"},{"name":"jbolda","email":"me@jacobbolda.com"},{"name":"cowboyd","email":"cowboyd@frontside.com"},{"name":"amirsa12","email":"amirhosseinpr184@gmail.com"},{"name":"dreyfus92","email":"paulvi92@gmail.com"},{"name":"aslemammad","email":"zorofight94@gmail.com"},{"name":"43081j","email":"43081james@gmail.com"}],"homepage":"https://github.com/bombshell-dev/tty#readme","bugs":{"url":"https://github.com/bombshell-dev/tty/issues"},"dist":{"shasum":"8a54104ae702996da2a5d5f3aa086b1a666f95be","tarball":"https://registry.npmjs.org/@bomb.sh/tty/-/tty-0.8.0.tgz","fileCount":23,"integrity":"sha512-tEE0mtqnEp2uJt4kQS5AjdghCD5KN4BobqHOlfhaiNyrigxFBKk61XkDy//4D7V/AejrTvubh44B4NV35lsnxw==","signatures":[{"sig":"MEUCIAHv5dgqdmQHtGVKBiP4lrtUQ2yPVVYmzyigJUhyfkUhAiEA6ZFVv48JBVee98KbNFudX7c6hLY0R+4FaHM85PfSCH4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bomb.sh%2ftty@0.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":134820},"type":"module","module":"./esm/mod.js","engines":{"node":">= 22"},"exports":{".":{"import":"./esm/mod.js"}},"scripts":{},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f10e0bb9-51fa-4201-95d9-c578cd8f200a"}},"repository":{"url":"git+https://github.com/bombshell-dev/tty.git","type":"git"},"_npmVersion":"11.13.0","description":"A terminal rendering backend for Clay, compiled to WebAssembly","directories":{},"sideEffects":false,"_nodeVersion":"24.17.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/tty_0.8.0_1782454467279_0.13736074479402327","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"_id":"@bomb.sh/tty@0.9.0","bugs":{"url":"https://github.com/bombshell-dev/tty/issues"},"dist":{"shasum":"b3f6482dcb1033bd9892f1388955a9c96b67bd76","tarball":"https://registry.npmjs.org/@bomb.sh/tty/-/tty-0.9.0.tgz","fileCount":23,"integrity":"sha512-1fX9lgwdc+kGRQeVEYwv7cJb5i855ysvB/TMCC3/wnnjMiKLZjWew6LLnsAMq4pLzkweivYw+zwYNZRUVhjoBA==","signatures":[{"sig":"MEUCIHvs0d93NMZKaWvWlyJavn5AbrygY9a/WnVosT3jN2ZMAiEA0lR4bVuprq0b3cw3abd2VD5kwjVVpfl+EbsdBNkxL8g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICopYXvkaM6t8f+BgURmVgBJOHQQUymBj73oyXdhjAfPAiEA0upAxghRVt87EDuWQY16/01R+kZzF+OTB72QwCBSI18="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bomb.sh%2ftty@0.9.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":112934},"name":"@bomb.sh/tty","type":"module","module":"./esm/mod.js","engines":{"node":">= 22"},"exports":{".":{"import":"./esm/mod.js"}},"gitHead":"7fb1097b19282c480092c2653dff3133fc001bcf","license":"MIT","scripts":{},"version":"0.9.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f10e0bb9-51fa-4201-95d9-c578cd8f200a"}},"homepage":"https://github.com/bombshell-dev/tty#readme","repository":{"url":"git+https://github.com/bombshell-dev/tty.git","type":"git"},"_npmVersion":"11.19.0","description":"A terminal rendering backend for Clay, compiled to WebAssembly","directories":{},"maintainers":[{"name":"natemoo-re","email":"nate@natemoo.re"},{"name":"jbolda","email":"me@jacobbolda.com"},{"name":"cowboyd","email":"cowboyd@frontside.com"},{"name":"amirsa12","email":"amirhosseinpr184@gmail.com"},{"name":"dreyfus92","email":"paulvi92@gmail.com"},{"name":"aslemammad","email":"zorofight94@gmail.com"},{"name":"43081j","email":"43081james@gmail.com"}],"sideEffects":false,"_nodeVersion":"24.20.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tty_0.9.0_1788562268201_0.4607290061359033"}}},"time":{"created":"2026-06-18T20:24:58.205Z","modified":"2026-09-04T22:51:08.735Z","0.0.0-register.0":"2026-06-18T20:24:58.525Z","0.7.0":"2026-06-18T20:33:21.441Z","0.8.0":"2026-06-26T06:14:27.446Z","0.9.0":"2026-09-04T22:51:08.305Z"},"bugs":{"url":"https://github.com/bombshell-dev/tty/issues"},"license":"MIT","homepage":"https://github.com/bombshell-dev/tty#readme","repository":{"url":"git+https://github.com/bombshell-dev/tty.git","type":"git"},"description":"A terminal rendering backend for Clay, compiled to WebAssembly","maintainers":[{"name":"natemoo-re","email":"nate@natemoo.re"},{"name":"jbolda","email":"me@jacobbolda.com"},{"name":"cowboyd","email":"cowboyd@frontside.com"},{"name":"amirsa12","email":"amirhosseinpr184@gmail.com"},{"name":"dreyfus92","email":"paulvi92@gmail.com"},{"name":"aslemammad","email":"zorofight94@gmail.com"},{"name":"43081j","email":"43081james@gmail.com"}],"readme":"# @bomb.sh/tty\n\nA low-level, platform-independent terminal renderer and event parser for\nJavaScript. You can use `@bomb.sh/tty` directly, or as the foundation for your\nown framework.\n\n## Features\n\n**Declarative terminal UI** — Build terminal interfaces the same way you'd build\na web page. `@bomb.sh/tty` uses [Clay](https://github.com/nicbarker/clay) under\nthe hood, giving you flexbox-like layout, pointer detection, and scroll\ncontainers — all rendered to the terminal as box-drawing characters and ANSI\nescape sequences.\n\n**Zero I/O** — `@bomb.sh/tty` never reads stdin or writes stdout. You feed it\nbytes and get bytes back. This makes it trivially embeddable in any framework,\nany runtime, any event loop. There are no opinions about how you do I/O, just\npure computation.\n\n**Runs everywhere** — The entire engine is compiled to WebAssembly, so\n`@bomb.sh/tty` will run anywhere JavaScript runs with no native dependencies,\nand no build step for consumers.\n\n### Examples\n\nSee this keyboard example and more in the [examples folder](examples/README.md).\nThis demo uses `@bomb.sh/tty` for all layout and input parsing.\n\n#### Keyboard Events\n\nThe input parser decodes raw terminal bytes into structured events. Here you can\nsee each key event as the string \"hello world\" is typed.\n\n![Keyboard events demo](examples/keyboard/keyboard-key-events.gif)\n\n#### Pointer Events\n\nHere we see hover styles applied to UI elements in response to the pointer\nstate. Clay drives the hit testing; no manual coordinate math required.\n\n![Pointer events demo](examples/keyboard/keyboard-pointer-events.gif)\n\n## Architecture\n\n`@bomb.sh/tty` does not do any I/O itself. On the ouput side, it converts UI\nelements into a raw sequence of bytes and pointer events, and on the input side,\nit converts a stream of raw bytes into structured events.\n\n### Output\n\nWith every frame, the entire UI tree is packed into a flat byte array and sent\nto WASM in a single call. On the C side, Clay runs layout, render commands are\nwalked into a cell buffer, and the buffer is diffed against the previous frame.\nOnly the cells that actually changed produce output. The result is an ANSI\nescape sequence that can be written directly to stdout. One trip to WASM per\nframe, double buffered, and only the bytes that need to change hit the output\nstream.\n\nBecause the WASM module is pure computation with no I/O, it runs anywhere\nWebAssembly does: Deno, Node, Bun, browsers, or any other runtime.\n\n```\n TypeScript                        WASM (C)\n+---------------+                +---------------------------+\n|               |  Uint32Array   |                           |\n| UI ops...     | =============> | Clay layout               |\n|               |                |   -> render commands      |\n+---------------+                |   -> cell buffer (back)   |\n                                 |   -> diff against (front) |\n                                 |   -> escape bytes         |\n+---------------+                |                           |\n|               | ANSI byte array|                           |\n| stdout.write  | <============= |                           |\n|               |                |                           |\n+---------------+                +---------------------------+\n```\n\n### Input\n\nRaw bytes from stdin are fed into a WASM-based parser that recognizes VT/ANSI\nescape sequences, UTF-8 codepoints, and mouse protocols (VT200, SGR, urxvt). The\nparser maintains its own internal buffer so partial sequences that arrive across\nread boundaries are reassembled automatically. A lone ESC byte is held for a\nconfigurable latency window (default 25ms) before being emitted, giving\nmulti-byte sequences time to arrive.\n\n```\n TypeScript                        WASM (C)\n+---------------+                +---------------------------+\n|               |  raw byte array|                           |\n| stdin.read    | =============> | trie match (keys/seqs)    |\n|               |                |   -> mouse protocol       |\n|               |                |   -> UTF-8 decode         |\n+---------------+                |   -> ESC codes            |\n                                 |                           |\n+---------------+                |                           |\n|               |  events[]      |                           |\n| KeyEvent      | <============= |                           |\n| MouseDownEvent|                |                           |\n| MouseUpEvent  |                +---------------------------+\n| MouseMoveEvent|\n| WheelEvent    |\n| ResizeEvent   |\n+---------------+\n```\n\n## Usage\n\n### Rendering\n\nTo render this:\n\n```\n╭───────────────╮\n│ Hello, World! │\n╰───────────────╯\n```\n\n```typescript\nimport { close, createTerm, open, rgba, text } from \"@bomb.sh/tty\";\n\nlet term = await createTerm({ width: 80, height: 24 });\n\nlet { output } = term.render([\n  open(\"greeting\", {\n    layout: { padding: { left: 1, right: 1, top: 1, bottom: 1 } },\n    border: {\n      color: rgba(0, 255, 0),\n      left: 1,\n      right: 1,\n      top: 1,\n      bottom: 1,\n    },\n    cornerRadius: { tl: 1, tr: 1, bl: 1, br: 1 },\n  }),\n  text(\"Hello, World!\"),\n  close(),\n]);\n\nprocess.stdout.write(output);\n```\n\n### Pointer detection\n\nPass pointer state to `render()` to have `@bomb.sh/tty` do hit detection and\nreturn pointer events in addition to the byte sequence.\n\n```typescript\nimport { close, createTerm, fixed, grow, open, rgba, text } from \"@bomb.sh/tty\";\n\nlet term = await createTerm({ width: 80, height: 24 });\n\nlet { output, events } = term.render(\n  [\n    open(\"root\", {\n      layout: { width: grow(), height: grow(), direction: \"ltr\" },\n    }),\n    open(\"sidebar\", {\n      layout: { width: fixed(20), height: grow() },\n      bg: rgba(30, 30, 40),\n    }),\n    text(\"Sidebar\"),\n    close(),\n    open(\"main\", {\n      layout: { width: grow(), height: grow() },\n    }),\n    text(\"Main content\"),\n    close(),\n    close(),\n  ],\n  {\n    pointer: { x: mouseX, y: mouseY, down: mouseDown },\n  },\n);\n\nfor (let event of events) {\n  // { type: \"pointerenter\", id: \"sidebar\" }\n  // { type: \"pointerleave\", id: \"sidebar\" }\n  // { type: \"pointerclick\", id: \"main\" }\n  console.log(event);\n}\n\nprocess.stdout.write(output);\n```\n\n### Input parsing\n\n```typescript\nimport { createInput } from \"@bomb.sh/tty\";\n\nlet input = await createInput({ escLatency: 25 });\n\nprocess.stdin.setRawMode(true);\nlet timer: ReturnType<typeof setTimeout> | undefined;\n\nprocess.stdin.on(\"data\", (buf) => {\n  clearTimeout(timer);\n\n  let { events, pending } = input.scan(new Uint8Array(buf));\n\n  for (let event of events) {\n    dispatch(event);\n  }\n\n  // if a lone ESC is pending, wait and re-scan to flush it\n  if (pending) {\n    timer = setTimeout(() => {\n      let flush = input.scan();\n      for (let event of flush.events) {\n        dispatch(event);\n      }\n    }, pending.delay);\n  }\n});\n```\n\n## Development\n\nFor local source builds, toolchain setup, and `clay` submodule instructions, see\n[BUILD.md](BUILD.md).\n\nQuick local validation:\n\n```sh\nmake\ndeno task test\n```\n","readmeFilename":"README.md"}