mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 19:05:11 +00:00
- Switch to function based API - Anthropic SDK style async generator - Fully typed with escape hatches for custom models
161 lines
3.4 KiB
TypeScript
161 lines
3.4 KiB
TypeScript
import type { Terminal as XtermTerminalType } from "@xterm/headless";
|
|
import xterm from "@xterm/headless";
|
|
import type { Terminal } from "../src/terminal.js";
|
|
|
|
// Extract Terminal class from the module
|
|
const XtermTerminal = xterm.Terminal;
|
|
|
|
/**
|
|
* Virtual terminal for testing using xterm.js for accurate terminal emulation
|
|
*/
|
|
export class VirtualTerminal implements Terminal {
|
|
private xterm: XtermTerminalType;
|
|
private inputHandler?: (data: string) => void;
|
|
private resizeHandler?: () => void;
|
|
private _columns: number;
|
|
private _rows: number;
|
|
|
|
constructor(columns = 80, rows = 24) {
|
|
this._columns = columns;
|
|
this._rows = rows;
|
|
|
|
// Create xterm instance with specified dimensions
|
|
this.xterm = new XtermTerminal({
|
|
cols: columns,
|
|
rows: rows,
|
|
// Disable all interactive features for testing
|
|
disableStdin: true,
|
|
allowProposedApi: true,
|
|
});
|
|
}
|
|
|
|
start(onInput: (data: string) => void, onResize: () => void): void {
|
|
this.inputHandler = onInput;
|
|
this.resizeHandler = onResize;
|
|
// No need for raw mode in virtual terminal
|
|
}
|
|
|
|
stop(): void {
|
|
this.inputHandler = undefined;
|
|
this.resizeHandler = undefined;
|
|
}
|
|
|
|
write(data: string): void {
|
|
this.xterm.write(data);
|
|
}
|
|
|
|
get columns(): number {
|
|
return this._columns;
|
|
}
|
|
|
|
get rows(): number {
|
|
return this._rows;
|
|
}
|
|
|
|
// Test-specific methods not in Terminal interface
|
|
|
|
/**
|
|
* Simulate keyboard input
|
|
*/
|
|
sendInput(data: string): void {
|
|
if (this.inputHandler) {
|
|
this.inputHandler(data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resize the terminal
|
|
*/
|
|
resize(columns: number, rows: number): void {
|
|
this._columns = columns;
|
|
this._rows = rows;
|
|
this.xterm.resize(columns, rows);
|
|
if (this.resizeHandler) {
|
|
this.resizeHandler();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for all pending writes to complete. Viewport and scroll buffer will be updated.
|
|
*/
|
|
async flush(): Promise<void> {
|
|
// Write an empty string to ensure all previous writes are flushed
|
|
return new Promise<void>((resolve) => {
|
|
this.xterm.write("", () => resolve());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Flush and get viewport - convenience method for tests
|
|
*/
|
|
async flushAndGetViewport(): Promise<string[]> {
|
|
await this.flush();
|
|
return this.getViewport();
|
|
}
|
|
|
|
/**
|
|
* Get the visible viewport (what's currently on screen)
|
|
* Note: You should use getViewportAfterWrite() for testing after writing data
|
|
*/
|
|
getViewport(): string[] {
|
|
const lines: string[] = [];
|
|
const buffer = this.xterm.buffer.active;
|
|
|
|
// Get only the visible lines (viewport)
|
|
for (let i = 0; i < this.xterm.rows; i++) {
|
|
const line = buffer.getLine(buffer.viewportY + i);
|
|
if (line) {
|
|
lines.push(line.translateToString(true));
|
|
} else {
|
|
lines.push("");
|
|
}
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
|
|
/**
|
|
* Get the entire scroll buffer
|
|
*/
|
|
getScrollBuffer(): string[] {
|
|
const lines: string[] = [];
|
|
const buffer = this.xterm.buffer.active;
|
|
|
|
// Get all lines in the buffer (including scrollback)
|
|
for (let i = 0; i < buffer.length; i++) {
|
|
const line = buffer.getLine(i);
|
|
if (line) {
|
|
lines.push(line.translateToString(true));
|
|
} else {
|
|
lines.push("");
|
|
}
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
|
|
/**
|
|
* Clear the terminal viewport
|
|
*/
|
|
clear(): void {
|
|
this.xterm.clear();
|
|
}
|
|
|
|
/**
|
|
* Reset the terminal completely
|
|
*/
|
|
reset(): void {
|
|
this.xterm.reset();
|
|
}
|
|
|
|
/**
|
|
* Get cursor position
|
|
*/
|
|
getCursorPosition(): { x: number; y: number } {
|
|
const buffer = this.xterm.buffer.active;
|
|
return {
|
|
x: buffer.cursorX,
|
|
y: buffer.cursorY,
|
|
};
|
|
}
|
|
}
|