clanker-agent/packages/tui/test/tui-overlay-style-leak.test.ts
Harivansh Rathi 0250f72976 move pi-mono into companion-cloud as apps/companion-os
- Copy all pi-mono source into apps/companion-os/
- Update Dockerfile to COPY pre-built binary instead of downloading from GitHub Releases
- Update deploy-staging.yml to build pi from source (bun compile) before Docker build
- Add apps/companion-os/** to path triggers
- No more cross-repo dispatch needed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 09:22:50 -08:00

79 lines
2.3 KiB
TypeScript

import assert from "node:assert";
import { describe, it } from "node:test";
import type { Terminal as XtermTerminalType } from "@xterm/headless";
import { type Component, TUI } from "../src/tui.js";
import { VirtualTerminal } from "./virtual-terminal.js";
class StaticLines implements Component {
constructor(private readonly lines: string[]) {}
render(): string[] {
return this.lines;
}
invalidate(): void {}
}
class StaticOverlay implements Component {
constructor(private readonly line: string) {}
render(): string[] {
return [this.line];
}
invalidate(): void {}
}
function getCellItalic(
terminal: VirtualTerminal,
row: number,
col: number,
): number {
const xterm = (terminal as unknown as { xterm: XtermTerminalType }).xterm;
const buffer = xterm.buffer.active;
const line = buffer.getLine(buffer.viewportY + row);
assert.ok(line, `Missing buffer line at row ${row}`);
const cell = line.getCell(col);
assert.ok(cell, `Missing cell at row ${row} col ${col}`);
return cell.isItalic();
}
async function renderAndFlush(
tui: TUI,
terminal: VirtualTerminal,
): Promise<void> {
tui.requestRender(true);
await new Promise<void>((resolve) => process.nextTick(resolve));
await terminal.flush();
}
describe("TUI overlay compositing", () => {
it("should not leak styles when a trailing reset sits beyond the last visible column (no overlay)", async () => {
const width = 20;
const baseLine = `\x1b[3m${"X".repeat(width)}\x1b[23m`;
const terminal = new VirtualTerminal(width, 6);
const tui = new TUI(terminal);
tui.addChild(new StaticLines([baseLine, "INPUT"]));
tui.start();
await renderAndFlush(tui, terminal);
assert.strictEqual(getCellItalic(terminal, 1, 0), 0);
tui.stop();
});
it("should not leak styles when overlay slicing drops trailing SGR resets", async () => {
const width = 20;
const baseLine = `\x1b[3m${"X".repeat(width)}\x1b[23m`;
const terminal = new VirtualTerminal(width, 6);
const tui = new TUI(terminal);
tui.addChild(new StaticLines([baseLine, "INPUT"]));
tui.showOverlay(new StaticOverlay("OVR"), { row: 0, col: 5, width: 3 });
tui.start();
await renderAndFlush(tui, terminal);
assert.strictEqual(getCellItalic(terminal, 1, 0), 0);
tui.stop();
});
});