mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
feat: add xterm-zerolag-input standalone library
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>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 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();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user