Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export interface ITerminalOptions {
// Scrolling options
smoothScrollDuration?: number; // Duration in ms for smooth scroll animation (default: 100, 0 = instant)

// Emit terminal-generated responses through onData (default: true)
//
// Some host applications answer terminal queries at the PTY boundary instead
// of in the renderer. Disable this to keep parser-generated replies, such as
// DSR responses, out of the same stream as user keyboard input.
emitTerminalResponses?: boolean;

// Internal: Ghostty WASM instance (optional, for test isolation)
// If not provided, uses the module-level instance from init()
ghostty?: Ghostty;
Expand Down
28 changes: 28 additions & 0 deletions lib/terminal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,34 @@ describe('Terminal', () => {
disposable.dispose();
});

test('emits terminal query responses through onData by default', async () => {
const term = await createIsolatedTerminal();
term.open(container!);

const receivedData: string[] = [];
term.onData((data) => receivedData.push(data));

term.write('\x1b[5n');

expect(receivedData).toContain('\x1b[0n');

term.dispose();
});

test('can keep terminal query responses out of onData', async () => {
const term = await createIsolatedTerminal({ emitTerminalResponses: false });
term.open(container!);

const receivedData: string[] = [];
term.onData((data) => receivedData.push(data));

term.write('\x1b[5n');

expect(receivedData).toEqual([]);

term.dispose();
});

test('onResize fires when terminal is resized', async () => {
const term = await createIsolatedTerminal({ cols: 80, rows: 24 });
term.open(container!);
Expand Down
10 changes: 7 additions & 3 deletions lib/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export class Terminal implements ITerminalCore {
convertEol: options.convertEol ?? false,
disableStdin: options.disableStdin ?? false,
smoothScrollDuration: options.smoothScrollDuration ?? 100, // Default: 100ms smooth scroll
emitTerminalResponses: options.emitTerminalResponses ?? true,
};

// Wrap in Proxy to intercept runtime changes (xterm.js compatibility)
Expand Down Expand Up @@ -560,9 +561,12 @@ export class Terminal implements ITerminalCore {
// Write directly to WASM terminal (handles VT parsing internally)
this.wasmTerm!.write(data);

// Process any responses generated by the terminal (e.g., DSR cursor position)
// These need to be sent back to the PTY via onData
this.processTerminalResponses();
// Process any responses generated by the terminal (e.g., DSR cursor position).
// These are useful for direct browser PTY integrations, but embedders that
// answer terminal queries server-side can opt out to keep onData user-only.
if (this.options.emitTerminalResponses) {
this.processTerminalResponses();
}

// Check for bell character (BEL, \x07)
// WASM doesn't expose bell events, so we detect it in the data stream
Expand Down