char-slop/berry

multipurpose self-modifiable agentic harness

git clone https://git.t4t.associates/char-slop/berry

Charlotte Somadd terminal extension6334b24

main
3.6 KiB89 linesraw
1import { assert, assertEquals } from "@std/assert";
2import { existsSync } from "node:fs";
3import { Ghostty, type KeyInput } from "./ghostty.ts";
4
5const WASM = new URL("./ghostty-vt.wasm", import.meta.url);
6const built = existsSync(WASM);
7const load = async () => await Ghostty.load(await Deno.readFile(WASM));
8const encode = (text: string) => new TextEncoder().encode(text);
9const decode = (bytes: Uint8Array | null) => bytes && new TextDecoder().decode(bytes);
10const press = (code: string, key: string, mods: Partial<KeyInput> = {}): KeyInput => ({
11  code,
12  key,
13  shiftKey: false,
14  ctrlKey: false,
15  altKey: false,
16  metaKey: false,
17  repeat: false,
18  ...mods,
19});
20
21Deno.test({
22  name: "a terminal draws what it is written in runs of one style, and then only what changes",
23  ignore: !built,
24  async fn() {
25    const terminal = (await load()).terminal(20, 4);
26    terminal.write(encode("plain \x1b[1;31mbold red\x1b[0m\r\n\x1b[7minverse\x1b[0m 🦀 x   "));
27    const first = terminal.frame();
28    assertEquals(first.cols, 20);
29    assertEquals(first.rows, 4);
30    assertEquals(first.changed.size, 4);
31    assertEquals(first.changed.get(0), [
32      { text: "plain " },
33      { text: "bold red", bold: true, fg: first.changed.get(0)![1].fg },
34    ]);
35    const red = first.changed.get(0)![1].fg!;
36    assert(red[0] > red[1] && red[0] > red[2], "red is red");
37    const [inverse, space, crab, rest] = first.changed.get(1)!;
38    assertEquals(inverse.text, "inverse");
39    assert(inverse.fg && inverse.bg, "inverse swaps in the defaults");
40    assertEquals([space.text, crab, rest.text], [" ", { text: "🦀", wide: true }, " x"]);
41    assertEquals(first.changed.get(2), []);
42    assertEquals(first.cursor, { x: 15, y: 1, style: "block" });
43
44    assertEquals(terminal.frame().changed.size, 0);
45    terminal.write(encode("\r\nnext"));
46    assertEquals([...terminal.frame().changed.keys()], [1, 2]);
47    terminal.free();
48  },
49});
50
51Deno.test({
52  name: "a snapshot carries a terminal, scrollback and unfinished sequences included, to another",
53  ignore: !built,
54  async fn() {
55    const ghostty = await load();
56    const terminal = ghostty.terminal(10, 2);
57    terminal.write(encode("one\r\ntwo\r\nthree\r\n\x1b[3"));
58    const restored = ghostty.restore(terminal.snapshot());
59    assertEquals(restored.text(), terminal.text());
60    // the half-written sequence finishes in the copy, as it would have in the original
61    restored.write(encode("1mred"));
62    assertEquals(restored.frame().changed.get(1)!.at(-1)!.text, "red");
63    restored.scroll(-1);
64    assertEquals(restored.frame().changed.get(0), [{ text: "two" }]);
65  },
66});
67
68Deno.test({
69  name:
70    "keys encode as the program's modes ask, pastes bracket when asked, and queries are answered",
71  ignore: !built,
72  async fn() {
73    const terminal = (await load()).terminal(80, 24);
74    assertEquals(decode(terminal.key(press("KeyA", "a"))), "a");
75    assertEquals(decode(terminal.key(press("KeyC", "c", { ctrlKey: true }))), "\x03");
76    assertEquals(decode(terminal.key(press("ArrowUp", "ArrowUp"))), "\x1b[A");
77    assertEquals(decode(terminal.key(press("Enter", "Enter"))), "\r");
78    assertEquals(terminal.key(press("ShiftLeft", "Shift", { shiftKey: true })), null);
79    assertEquals(decode(terminal.paste("ls")), "ls");
80
81    const answers: string[] = [];
82    terminal.answer((data) => answers.push(decode(data)!));
83    terminal.write(encode("\x1b[?1h\x1b[?2004h\x1b[c"));
84    assertEquals(decode(terminal.key(press("ArrowUp", "ArrowUp"))), "\x1bOA");
85    assertEquals(decode(terminal.paste("ls")), "\x1b[200~ls\x1b[201~");
86    assertEquals(answers.length, 1);
87    assert(answers[0].startsWith("\x1b[?"), "a device attributes report");
88  },
89});