@bomb.sh/tty (英语)
一个低等、平台独立的终端渲染器和事件解析器
Java脚本. 你用得着 @bomb.sh/tty 直接,或作为您的基础
拥有的框架.
特征
宣告终端 UI ——以你构建的方式构建终端接口
a 网页. @bomb.sh/tty 用途 克莱 下级
引擎盖, 给你类似弹性盒的布局, 指针检测和滚动
集装箱——全部作为箱面绘图字符和ANSI 提供给终端
逃脱序列.
无 I/ 欧 —— 说 @bomb.sh/tty 从不读 Stdin 或写 stdout 。 你喂饱它
字节并获得字节回. 这使得它微不足道地嵌入任何框架,
任何运行时间,任何事件循环。 你如何做 I/O没有意见,只是
纯计算.
到处跑 整个引擎被编译到WebAssembly,所以
@bomb.sh/tty 将运行在 JavaScript 运行的任何地方, 没有本地依赖,
也不给消费者任何建设步骤.
实例
参见此键盘示例和更多 示例文件夹。 。 。 .
此演示使用 @bomb.sh/tty 用于所有布局和输入解析.
键盘事件
输入解码器将原始终端字节解码为结构化事件. 给你 将每个关键事件作为字符串“你好世界”键入.

指针事件
在这里,我们看到针对指针的UI元素应用的悬浮样式 状态。 Clay驱动命中测试;不需要手动坐标数学.

建筑
@bomb.sh/tty 没有做任何I/O本身。 在自动侧面,它转换UI
元素进入字节和指针事件的生序,在输入边,
它将一串生字节的流转换成结构化的事件.
产出
每个框,整个UI树被打包成平整字节数组并发送 给WASM的电话。 C方面,克莱运行布局,渲染命令是 走入了细胞缓冲器,缓冲器被分解出与上个帧相对. 只有实际改变的单元格产生输出. 结果是一个ANSI 可以直接写入 stdout 的逃生序列 。 每趟去WASM的旅行 帧, 双缓冲, 只有需要更改的字节键击中输出 流线.
因为WASM模块是纯粹的计算,没有I/O,它运行在任何地方 WebAssembly does: Deno, Node, Bun,浏览器,或任何其他运行时间.
TypeScript WASM (C)
+---------------+ +---------------------------+
| | Uint32Array | |
| UI ops... | =============> | Clay layout |
| | | -> render commands |
+---------------+ | -> cell buffer (back) |
| -> diff against (front) |
| -> escape bytes |
+---------------+ | |
| | ANSI byte array| |
| stdout.write | <============= | |
| | | |
+---------------+ +---------------------------+
投入
来自 stdin 的 Raw 字节输入到基于 WASM 的解析器中,该解析器会识别 VT/ ANSI 逃脱序列,UTF-8代码点,以及鼠标协议(VT200,SGR,urxvt). 这个 parser 维护自己的内部缓冲, 这样部分序列会到达 读取边界自动重新组合。 一个单独的ESC字节为 释放前可配置的耐用窗口(默认为 25ms) 多字节序列时间即将到来.
TypeScript WASM (C)
+---------------+ +---------------------------+
| | raw byte array| |
| stdin.read | =============> | trie match (keys/seqs) |
| | | -> mouse protocol |
| | | -> UTF-8 decode |
+---------------+ | -> ESC codes |
| |
+---------------+ | |
| | events[] | |
| KeyEvent | <============= | |
| MouseDownEvent| | |
| MouseUpEvent | +---------------------------+
| MouseMoveEvent|
| WheelEvent |
| ResizeEvent |
+---------------+
使用量
渲染
为此:
╭───────────────╮
│ Hello, World! │
╰───────────────╯
import { close, createTerm, open, rgba, text } from "@bomb.sh/tty";
let term = await createTerm({ width: 80, height: 24 });
let { output } = term.render([
open("greeting", {
layout: { padding: { left: 1, right: 1, top: 1, bottom: 1 } },
border: {
color: rgba(0, 255, 0),
left: 1,
right: 1,
top: 1,
bottom: 1,
},
cornerRadius: { tl: 1, tr: 1, bl: 1, br: 1 },
}),
text("Hello, World!"),
close(),
]);
process.stdout.write(output);
指针检测
通过指针状态 render() 拥有 @bomb.sh/tty 进行命中检测
除字节序列外的返回指针事件.
import { close, createTerm, fixed, grow, open, rgba, text } from "@bomb.sh/tty";
let term = await createTerm({ width: 80, height: 24 });
let { output, events } = term.render(
[
open("root", {
layout: { width: grow(), height: grow(), direction: "ltr" },
}),
open("sidebar", {
layout: { width: fixed(20), height: grow() },
bg: rgba(30, 30, 40),
}),
text("Sidebar"),
close(),
open("main", {
layout: { width: grow(), height: grow() },
}),
text("Main content"),
close(),
close(),
],
{
pointer: { x: mouseX, y: mouseY, down: mouseDown },
},
);
for (let event of events) {
// { type: "pointerenter", id: "sidebar" }
// { type: "pointerleave", id: "sidebar" }
// { type: "pointerclick", id: "main" }
console.log(event);
}
process.stdout.write(output);
输入解析
import { createInput } from "@bomb.sh/tty";
let input = await createInput({ escLatency: 25 });
process.stdin.setRawMode(true);
let timer: ReturnType<typeof setTimeout> | undefined;
process.stdin.on("data", (buf) => {
clearTimeout(timer);
let { events, pending } = input.scan(new Uint8Array(buf));
for (let event of events) {
dispatch(event);
}
// if a lone ESC is pending, wait and re-scan to flush it
if (pending) {
timer = setTimeout(() => {
let flush = input.scan();
for (let event of flush.events) {
dispatch(event);
}
}, pending.delay);
}
});
发展
用于本地源构建、工具链设置和 clay 子模块指令,参见
贝鲁特,md。 。 。 .
快速本地验证 :
make
deno task test