mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 15:09:42 +02:00
Extract Claudeman's local echo overlay into a reusable xterm.js addon at packages/xterm-zerolag-input/. Provides instant keystroke feedback via a DOM overlay, eliminating perceived input latency over high-RTT connections (SSH, mobile, cloud IDEs). - Zero dependencies, compatible with xterm v5.x and @xterm/xterm v5.4+ - Configurable prompt detection (character, regex, or custom function) - Flushed text tracking for tab-switch / deferred echo scenarios - Per-character grid-aligned rendering matching xterm's canvas output - 56 tests passing (prompt finder, overlay renderer, full addon lifecycle) - Dual CJS/ESM build with full TypeScript declarations Claudeman source is unchanged — migration to consume this lib is a separate follow-up. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
122 lines
3.3 KiB
TypeScript
122 lines
3.3 KiB
TypeScript
/**
|
|
* Mock terminal factory for unit tests.
|
|
*
|
|
* Creates a minimal Terminal-like object that satisfies the addon's
|
|
* requirements without needing a real xterm.js instance or DOM renderer.
|
|
*/
|
|
|
|
interface MockLine {
|
|
translateToString(_trimRight?: boolean): string;
|
|
}
|
|
|
|
interface MockBufferOptions {
|
|
lines: string[];
|
|
viewportY?: number;
|
|
baseY?: number;
|
|
cursorX?: number;
|
|
cursorY?: number;
|
|
}
|
|
|
|
interface MockTerminalOptions {
|
|
buffer?: MockBufferOptions;
|
|
cols?: number;
|
|
rows?: number;
|
|
fontFamily?: string;
|
|
fontSize?: number;
|
|
fontWeight?: string | number;
|
|
theme?: {
|
|
background?: string;
|
|
foreground?: string;
|
|
cursor?: string;
|
|
};
|
|
cellWidth?: number;
|
|
cellHeight?: number;
|
|
}
|
|
|
|
export function createMockTerminal(opts: MockTerminalOptions = {}) {
|
|
const bufOpts = opts.buffer ?? { lines: ['$ '] };
|
|
const lines = bufOpts.lines;
|
|
const viewportY = bufOpts.viewportY ?? 0;
|
|
const baseY = bufOpts.baseY ?? viewportY;
|
|
const cols = opts.cols ?? 80;
|
|
const rows = opts.rows ?? Math.max(lines.length, 24);
|
|
const cellW = opts.cellWidth ?? 8.4;
|
|
const cellH = opts.cellHeight ?? 17;
|
|
|
|
const mockLines: MockLine[] = lines.map((text) => ({
|
|
translateToString: () => text,
|
|
}));
|
|
|
|
// Create minimal DOM structure
|
|
const element = document.createElement('div');
|
|
element.className = 'terminal xterm';
|
|
|
|
const viewport = document.createElement('div');
|
|
viewport.className = 'xterm-viewport';
|
|
|
|
const screen = document.createElement('div');
|
|
screen.className = 'xterm-screen';
|
|
screen.style.position = 'relative';
|
|
|
|
const xtermRows = document.createElement('div');
|
|
xtermRows.className = 'xterm-rows';
|
|
|
|
element.appendChild(viewport);
|
|
element.appendChild(screen);
|
|
screen.appendChild(xtermRows);
|
|
|
|
// Append to document so getComputedStyle works
|
|
document.body.appendChild(element);
|
|
|
|
const terminal = {
|
|
element,
|
|
cols,
|
|
rows,
|
|
options: {
|
|
fontFamily: opts.fontFamily ?? 'monospace',
|
|
fontSize: opts.fontSize ?? 14,
|
|
fontWeight: opts.fontWeight ?? 'normal',
|
|
theme: opts.theme ?? {},
|
|
},
|
|
buffer: {
|
|
active: {
|
|
viewportY,
|
|
baseY,
|
|
cursorX: bufOpts.cursorX ?? 0,
|
|
cursorY: bufOpts.cursorY ?? 0,
|
|
getLine: (absRow: number): MockLine | undefined => {
|
|
return mockLines[absRow - viewportY];
|
|
},
|
|
},
|
|
},
|
|
_core: {
|
|
_renderService: {
|
|
dimensions: {
|
|
css: {
|
|
cell: { width: cellW, height: cellH },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
// Simulate loadAddon
|
|
loadAddon(addon: { activate: (t: unknown) => void }) {
|
|
addon.activate(this);
|
|
},
|
|
};
|
|
|
|
return {
|
|
terminal,
|
|
/** Update buffer lines for subsequent calls */
|
|
setLines(newLines: string[]) {
|
|
mockLines.length = 0;
|
|
for (const text of newLines) {
|
|
mockLines.push({ translateToString: () => text });
|
|
}
|
|
},
|
|
/** Clean up DOM */
|
|
cleanup() {
|
|
element.remove();
|
|
},
|
|
};
|
|
}
|