summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/utils/kittyProtocolDetector.ts
diff options
context:
space:
mode:
authorDeepankar Sharma <[email protected]>2025-08-13 13:32:54 -0400
committerGitHub <[email protected]>2025-08-13 17:32:54 +0000
commit9c7fb870c1a7c80741fafdfc6837d4b92e373b2d (patch)
tree1e31f201520c9724b4659ee7a36c21215e6280b9 /packages/cli/src/ui/utils/kittyProtocolDetector.ts
parent74a13fb535b255797d6c9aa3499acfea6aadc58d (diff)
Add terminal setup command for Shift+Enter and Ctrl+Enter support (#3289)
Co-authored-by: jacob314 <[email protected]>
Diffstat (limited to 'packages/cli/src/ui/utils/kittyProtocolDetector.ts')
-rw-r--r--packages/cli/src/ui/utils/kittyProtocolDetector.ts105
1 files changed, 105 insertions, 0 deletions
diff --git a/packages/cli/src/ui/utils/kittyProtocolDetector.ts b/packages/cli/src/ui/utils/kittyProtocolDetector.ts
new file mode 100644
index 00000000..5d77943a
--- /dev/null
+++ b/packages/cli/src/ui/utils/kittyProtocolDetector.ts
@@ -0,0 +1,105 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+let detectionComplete = false;
+let protocolSupported = false;
+let protocolEnabled = false;
+
+/**
+ * Detects Kitty keyboard protocol support.
+ * Definitive document about this protocol lives at https://sw.kovidgoyal.net/kitty/keyboard-protocol/
+ * This function should be called once at app startup.
+ */
+export async function detectAndEnableKittyProtocol(): Promise<boolean> {
+ if (detectionComplete) {
+ return protocolSupported;
+ }
+
+ return new Promise((resolve) => {
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
+ detectionComplete = true;
+ resolve(false);
+ return;
+ }
+
+ const originalRawMode = process.stdin.isRaw;
+ if (!originalRawMode) {
+ process.stdin.setRawMode(true);
+ }
+
+ let responseBuffer = '';
+ let progressiveEnhancementReceived = false;
+ let checkFinished = false;
+
+ const handleData = (data: Buffer) => {
+ responseBuffer += data.toString();
+
+ // Check for progressive enhancement response (CSI ? <flags> u)
+ if (responseBuffer.includes('\x1b[?') && responseBuffer.includes('u')) {
+ progressiveEnhancementReceived = true;
+ }
+
+ // Check for device attributes response (CSI ? <attrs> c)
+ if (responseBuffer.includes('\x1b[?') && responseBuffer.includes('c')) {
+ if (!checkFinished) {
+ checkFinished = true;
+ process.stdin.removeListener('data', handleData);
+
+ if (!originalRawMode) {
+ process.stdin.setRawMode(false);
+ }
+
+ if (progressiveEnhancementReceived) {
+ // Enable the protocol
+ process.stdout.write('\x1b[>1u');
+ protocolSupported = true;
+ protocolEnabled = true;
+
+ // Set up cleanup on exit
+ process.on('exit', disableProtocol);
+ process.on('SIGTERM', disableProtocol);
+ }
+
+ detectionComplete = true;
+ resolve(protocolSupported);
+ }
+ }
+ };
+
+ process.stdin.on('data', handleData);
+
+ // Send queries
+ process.stdout.write('\x1b[?u'); // Query progressive enhancement
+ process.stdout.write('\x1b[c'); // Query device attributes
+
+ // Timeout after 50ms
+ setTimeout(() => {
+ if (!checkFinished) {
+ process.stdin.removeListener('data', handleData);
+ if (!originalRawMode) {
+ process.stdin.setRawMode(false);
+ }
+ detectionComplete = true;
+ resolve(false);
+ }
+ }, 50);
+ });
+}
+
+function disableProtocol() {
+ if (protocolEnabled) {
+ process.stdout.write('\x1b[<u');
+ protocolEnabled = false;
+ }
+}
+
+export function isKittyProtocolEnabled(): boolean {
+ return protocolEnabled;
+}
+
+export function isKittyProtocolSupported(): boolean {
+ return protocolSupported;
+}