Add terminal.ts module for headless terminal emulation
Creates new module providing terminal session management using @xterm/headless: - createTerminal(): spawn headless terminal emulator instances - serializeAsHTML(): export terminal state as HTML - disposeTerminal(): clean up resources This replaces stateless ANSI processing with proper VT emulation that tracks cursor position, screen buffer, and terminal attributes across output chunks.
This commit is contained in:
parent
5016cd9960
commit
c9908c87c3
4 changed files with 56 additions and 1 deletions
6
bun.lock
6
bun.lock
|
|
@ -5,6 +5,8 @@
|
|||
"": {
|
||||
"name": "clarc",
|
||||
"dependencies": {
|
||||
"@xterm/addon-serialize": "^0.14.0",
|
||||
"@xterm/headless": "^6.0.0",
|
||||
"bun-pty": "^0.4.8",
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -39,6 +41,10 @@
|
|||
|
||||
"@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
|
||||
|
||||
"@xterm/addon-serialize": ["@xterm/addon-serialize@0.14.0", "", {}, "sha512-uteyTU1EkrQa2Ux6P/uFl2fzmXI46jy5uoQMKEOM0fKTyiW7cSn0WrFenHm5vO5uEXX/GpwW/FgILvv3r0WbkA=="],
|
||||
|
||||
"@xterm/headless": ["@xterm/headless@6.0.0", "", {}, "sha512-5Yj1QINYCyzrZtf8OFIHi47iQtI+0qYFPHmouEfG8dHNxbZ9Tb9YGSuLcsEwj9Z+OL75GJqPyJbyoFer80a2Hw=="],
|
||||
|
||||
"bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-qyschsA03Qz+gou+apt6HNl6HnI+sJJLL4wLDke4iugsE6584CMupOtTY1n+2YC9nGVrEKUlTs99jjRLKgWnjQ=="],
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xterm/addon-serialize": "^0.14.0",
|
||||
"@xterm/headless": "^6.0.0",
|
||||
"bun-pty": "^0.4.8"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -494,7 +494,9 @@ const server = Bun.serve<SessionData>({
|
|||
// Determine if new tail is an incomplete control sequence and split
|
||||
const [body, carry] = splitAnsiCarryover(combined);
|
||||
if (carry) {
|
||||
console.debug(`Session ${sessionId}: ANSI carryover detected (${carry.length} bytes)`);
|
||||
console.debug(
|
||||
`Session ${sessionId}: ANSI carryover detected (${carry.length} bytes)`,
|
||||
);
|
||||
ansiCarryovers.set(sessionId, carry);
|
||||
} else if (prevCarry) {
|
||||
console.debug(`Session ${sessionId}: ANSI carryover resolved`);
|
||||
|
|
|
|||
45
src/terminal.ts
Normal file
45
src/terminal.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { SerializeAddon } from "@xterm/addon-serialize";
|
||||
import { Terminal } from "@xterm/headless";
|
||||
|
||||
export interface TerminalSession {
|
||||
terminal: Terminal;
|
||||
serialize: SerializeAddon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new headless terminal emulator instance
|
||||
* @param cols - Terminal width in columns
|
||||
* @param rows - Terminal height in rows
|
||||
* @returns Terminal session with emulator and serialization addon
|
||||
*/
|
||||
export function createTerminal(cols: number, rows: number): TerminalSession {
|
||||
const terminal = new Terminal({
|
||||
cols,
|
||||
rows,
|
||||
scrollback: 1000, // Keep 1000 lines of scrollback
|
||||
allowProposedApi: true,
|
||||
});
|
||||
|
||||
const serialize = new SerializeAddon();
|
||||
terminal.loadAddon(serialize);
|
||||
|
||||
return { terminal, serialize };
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize terminal screen buffer as HTML
|
||||
*/
|
||||
export function serializeAsHTML(session: TerminalSession): string {
|
||||
return session.serialize.serializeAsHTML({
|
||||
onlySelection: false,
|
||||
includeGlobalBackground: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up terminal resources
|
||||
*/
|
||||
export function disposeTerminal(session: TerminalSession): void {
|
||||
session.serialize.dispose();
|
||||
session.terminal.dispose();
|
||||
}
|
||||
Loading…
Reference in a new issue