Skip to content

Commit 3dd4aef

Browse files
authored
feat: add DSR response handling for nushell compatibility (#82)
1 parent 22f6d09 commit 3dd4aef

File tree

4 files changed

+418
-8
lines changed

4 files changed

+418
-8
lines changed

lib/ghostty.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,40 @@ export class GhosttyTerminal {
609609
return null; // TODO: Add hyperlink support
610610
}
611611

612+
/**
613+
* Check if there are pending responses from the terminal.
614+
* Responses are generated by escape sequences like DSR (Device Status Report).
615+
*/
616+
hasResponse(): boolean {
617+
return this.exports.ghostty_terminal_has_response(this.handle);
618+
}
619+
620+
/**
621+
* Read pending responses from the terminal.
622+
* Returns the response string, or null if no responses pending.
623+
*
624+
* Responses are generated by escape sequences that require replies:
625+
* - DSR 6 (cursor position): Returns \x1b[row;colR
626+
* - DSR 5 (operating status): Returns \x1b[0n
627+
*/
628+
readResponse(): string | null {
629+
if (!this.hasResponse()) return null;
630+
631+
const bufSize = 256; // Most responses are small
632+
const bufPtr = this.exports.ghostty_wasm_alloc_u8_array(bufSize);
633+
634+
try {
635+
const bytesRead = this.exports.ghostty_terminal_read_response(this.handle, bufPtr, bufSize);
636+
637+
if (bytesRead <= 0) return null;
638+
639+
const bytes = new Uint8Array(this.memory.buffer, bufPtr, bytesRead);
640+
return new TextDecoder().decode(bytes.slice());
641+
} finally {
642+
this.exports.ghostty_wasm_free_u8_array(bufPtr, bufSize);
643+
}
644+
}
645+
612646
/**
613647
* Query arbitrary terminal mode by number
614648
* @param mode Mode number (e.g., 25 for cursor visibility, 2004 for bracketed paste)

lib/terminal.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,10 @@ export class Terminal implements ITerminalCore {
539539
// Write directly to WASM terminal (handles VT parsing internally)
540540
this.wasmTerm!.write(data);
541541

542+
// Process any responses generated by the terminal (e.g., DSR cursor position)
543+
// These need to be sent back to the PTY via onData
544+
this.processTerminalResponses();
545+
542546
// Check for bell character (BEL, \x07)
543547
// WASM doesn't expose bell events, so we detect it in the data stream
544548
if (typeof data === 'string' && data.includes('\x07')) {
@@ -1721,6 +1725,29 @@ export class Terminal implements ITerminalCore {
17211725
animate();
17221726
}
17231727

1728+
/**
1729+
* Process any pending terminal responses and emit them via onData.
1730+
*
1731+
* This handles escape sequences that require the terminal to send a response
1732+
* back to the PTY, such as:
1733+
* - DSR 6 (cursor position): Shell sends \x1b[6n, terminal responds with \x1b[row;colR
1734+
* - DSR 5 (operating status): Shell sends \x1b[5n, terminal responds with \x1b[0n
1735+
*
1736+
* Without this, shells like nushell that rely on cursor position queries
1737+
* will hang waiting for a response that never comes.
1738+
*/
1739+
private processTerminalResponses(): void {
1740+
if (!this.wasmTerm) return;
1741+
1742+
// Read any pending responses from the WASM terminal
1743+
const response = this.wasmTerm.readResponse();
1744+
if (response) {
1745+
// Send response back to the PTY via onData
1746+
// This is the same path as user keyboard input
1747+
this.dataEmitter.fire(response);
1748+
}
1749+
}
1750+
17241751
/**
17251752
* Check for title changes in written data (OSC sequences)
17261753
* Simplified implementation - looks for OSC 0, 1, 2

lib/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,10 @@ export interface GhosttyWasmExports extends WebAssembly.Exports {
445445
bufLen: number
446446
): number; // Returns cells written or -1 on error
447447
ghostty_terminal_is_row_wrapped(terminal: TerminalHandle, row: number): number;
448+
449+
// Response API (for DSR and other terminal queries)
450+
ghostty_terminal_has_response(terminal: TerminalHandle): boolean;
451+
ghostty_terminal_read_response(terminal: TerminalHandle, bufPtr: number, bufLen: number): number; // Returns bytes written, 0 if no response, -1 on error
448452
}
449453

450454
// ============================================================================

0 commit comments

Comments
 (0)