char-slop/berry

multipurpose self-modifiable agentic harness

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

Charlotte Somterminal: render styled text runs in normal flowc52d3a7

main
3.6 KiB94 linesraw
1import { assert, assertEquals } from "@std/assert";
2import { existsSync } from "node:fs";
3import { withDom } from "@berry/util/dom.ts";
4import { Grid } from "./grid.ts";
5import { Ghostty } from "./ghostty.ts";
6
7Deno.test("rescaling the grid repaints cached cells without replacing selected text", async () => {
8  await withDom((window) => {
9    const rows = document.createElement("div");
10    const canvas = document.createElement("canvas");
11    const painted: number[][] = [];
12    Object.defineProperty(canvas, "getContext", {
13      value: () => ({
14        clearRect: () => painted.splice(0),
15        fillRect: (...rect: number[]) => painted.push(rect),
16      }),
17    });
18    const grid = new Grid(rows, canvas);
19    const metrics = { width: 200, height: 100, ratio: 1, cell: { width: 8.25, height: 18.5 } };
20    grid.fit(metrics);
21    const bg = [61, 34, 74] as const;
22    grid.update({
23      cols: 20,
24      rows: 2,
25      cursor: null,
26      changed: new Map([
27        [0, [{ text: "e\u0301", bg }, { text: "🦀", bg, wide: true }, { text: "x", bg }]],
28        [1, [{ text: "next", bg }]],
29      ]),
30    });
31    assertEquals(rows.firstChild!.textContent, "e\u0301🦀x");
32    const range = window.document.createRange();
33    const text = rows.firstChild!.firstChild!.firstChild!;
34    range.selectNodeContents(text as unknown as Parameters<typeof range.selectNodeContents>[0]);
35    window.getSelection().addRange(range);
36
37    for (const ratio of [1, 1.25, 1.5, 2, 0.8]) {
38      grid.fit({ ...metrics, ratio });
39      assertEquals(window.getSelection().toString(), "e\u0301");
40      assertEquals(rows.firstChild!.firstChild!.firstChild, text);
41      assertEquals(painted.length, 4);
42      const [first, wide, last, next] = painted;
43      assertEquals(first[0], 0);
44      assertEquals(first[0] + first[2], wide[0]);
45      assertEquals(wide[0] + wide[2], last[0]);
46      assertEquals(first[1] + first[3], next[1]);
47      assertEquals(last[0] + last[2], next[2]);
48      assert(wide[2] > first[2]);
49      assert(painted.flat().every(Number.isInteger));
50    }
51
52    grid.update({ cols: 20, rows: 2, cursor: null, changed: new Map() });
53    assertEquals(painted.length, 4);
54    assertEquals(rows.firstChild!.firstChild!.firstChild, text);
55  });
56});
57
58Deno.test({
59  name: "screen-filling backgrounds include blank cells through the screen edges",
60  ignore: !existsSync(new URL("./ghostty-vt.wasm", import.meta.url)),
61  async fn() {
62    const ghostty = await Ghostty.load(
63      await Deno.readFile(new URL("./ghostty-vt.wasm", import.meta.url)),
64    );
65    const terminal = ghostty.terminal(20, 4);
66    try {
67      terminal.write(new TextEncoder().encode(
68        "\x1b[?25l\x1b[48;2;61;34;74m" + " ".repeat(80) + "\x1b[Hé🦀x",
69      ));
70      const frame = terminal.frame();
71      await withDom(() => {
72        const canvas = document.createElement("canvas");
73        const painted: number[][] = [];
74        Object.defineProperty(canvas, "getContext", {
75          value: () => ({
76            clearRect: () => painted.splice(0),
77            fillRect: (...rect: number[]) => painted.push(rect),
78          }),
79        });
80        const grid = new Grid(document.createElement("div"), canvas);
81        grid.fit({ width: 165, height: 74, ratio: 1.25, cell: { width: 8.25, height: 18.5 } });
82        grid.update(frame);
83        assertEquals(
84          painted.reduce((area, [, , width, height]) => area + width * height, 0),
85          canvas.width * canvas.height,
86        );
87        assertEquals(Math.max(...painted.map(([x, , width]) => x + width)), canvas.width);
88        assertEquals(Math.max(...painted.map(([, y, , height]) => y + height)), canvas.height);
89      });
90    } finally {
91      terminal.free();
92    }
93  },
94});