tinky-termcap 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * tinky-termcap - Terminal capability detection for Tinky applications.
4
+ *
5
+ * This library provides React hooks and utilities for detecting terminal
6
+ * capabilities such as:
7
+ * - **Background color** - Detect light/dark themes via OSC 11
8
+ * - **Terminal name** - Identify the terminal emulator (xterm, kitty, etc.)
9
+ * - **Kitty keyboard protocol** - Enhanced keyboard input handling
10
+ * - **modifyOtherKeys** - Key sequence disambiguation (Ctrl+I vs Tab)
11
+ *
12
+ * @example Quick start
13
+ * ```tsx
14
+ * import { render } from "tinky";
15
+ * import { TermcapProvider, useTermcap } from "tinky-termcap";
16
+ *
17
+ * function App() {
18
+ * const { isReady, backgroundColor, terminalName, kittyProtocol } = useTermcap();
19
+ *
20
+ * if (!isReady) {
21
+ * return <Text>Detecting terminal capabilities...</Text>;
22
+ * }
23
+ *
24
+ * return (
25
+ * <Box flexDirection="column">
26
+ * <Text>Terminal: {terminalName ?? "Unknown"}</Text>
27
+ * <Text>Background: {backgroundColor ?? "Unknown"}</Text>
28
+ * <Text>Kitty Protocol: {kittyProtocol ? "Supported" : "Not supported"}</Text>
29
+ * </Box>
30
+ * );
31
+ * }
32
+ *
33
+ * render(
34
+ * <TermcapProvider>
35
+ * <App />
36
+ * </TermcapProvider>
37
+ * );
38
+ * ```
39
+ *
40
+ * @example Direct detection (without React)
41
+ * ```typescript
42
+ * import { detectTermcap } from "tinky-termcap";
43
+ *
44
+ * async function main() {
45
+ * process.stdin.setRawMode(true);
46
+ * const caps = await detectTermcap(process.stdin, process.stdout, 1000);
47
+ *
48
+ * console.log("Terminal:", caps.terminalName);
49
+ * console.log("Background:", caps.backgroundColor);
50
+ * console.log("Kitty:", caps.kittyProtocol);
51
+ * console.log("modifyOtherKeys:", caps.modifyOtherKeys);
52
+ * }
53
+ * ```
54
+ *
55
+ * @packageDocumentation
56
+ */
57
+ Object.defineProperty(exports, "__esModule", { value: true });
58
+ exports.DEFAULT_DETECTION_TIMEOUT = exports.detectTermcap = exports.useTermcap = exports.TermcapProvider = void 0;
59
+ /**
60
+ * React context provider for terminal capability detection.
61
+ *
62
+ * Wrap your application in `TermcapProvider` to enable automatic terminal
63
+ * capability detection. Child components can then use `useTermcap()` to
64
+ * access the detected capabilities.
65
+ *
66
+ * @see {@link TermcapProviderProps} for configuration options
67
+ */
68
+ var TermcapContext_js_1 = require("./contexts/TermcapContext.js");
69
+ Object.defineProperty(exports, "TermcapProvider", { enumerable: true, get: function () { return TermcapContext_js_1.TermcapProvider; } });
70
+ /**
71
+ * React hook for accessing terminal capabilities.
72
+ *
73
+ * Must be used within a `TermcapProvider`. Returns a `TermcapInfo` object
74
+ * containing the detected terminal capabilities.
75
+ *
76
+ * @see {@link TermcapInfo} for the shape of returned data
77
+ */
78
+ var use_termcap_js_1 = require("./hooks/use-termcap.js");
79
+ Object.defineProperty(exports, "useTermcap", { enumerable: true, get: function () { return use_termcap_js_1.useTermcap; } });
80
+ /**
81
+ * Low-level terminal capability detection function.
82
+ *
83
+ * Use this directly when you need capability detection outside of React,
84
+ * or when you need more control over the detection process.
85
+ */
86
+ var detect_termcap_js_1 = require("./utils/detect-termcap.js");
87
+ Object.defineProperty(exports, "detectTermcap", { enumerable: true, get: function () { return detect_termcap_js_1.detectTermcap; } });
88
+ /**
89
+ * Re-export additional types and constants for advanced usage.
90
+ */
91
+ var detect_termcap_js_2 = require("./utils/detect-termcap.js");
92
+ Object.defineProperty(exports, "DEFAULT_DETECTION_TIMEOUT", { enumerable: true, get: function () { return detect_termcap_js_2.DEFAULT_DETECTION_TIMEOUT; } });
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,mBAAmB;AACnB,OAAO,EACL,eAAe,EACf,UAAU,GAGX,MAAM,qBAAqB,CAAC;AAE7B,8CAA8C;AAC9C,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,eAAe,GAChB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,328 @@
1
+ /**
2
+ * @fileoverview Terminal capability detection utilities.
3
+ *
4
+ * This module provides functions and types for detecting terminal capabilities
5
+ * by sending escape sequence queries to the terminal and parsing responses.
6
+ * It supports detection of:
7
+ * - Background color (via OSC 11)
8
+ * - Terminal name/version (via XTVERSION)
9
+ * - Kitty keyboard protocol support
10
+ * - modifyOtherKeys mode support
11
+ *
12
+ * @example Basic usage with tinky
13
+ * ```tsx
14
+ * import { TermcapProvider, useTermcap } from "tinky-termcap";
15
+ *
16
+ * function App() {
17
+ * const { isReady, backgroundColor, kittyProtocol } = useTermcap();
18
+ *
19
+ * if (!isReady) return <Text>Detecting...</Text>;
20
+ *
21
+ * return (
22
+ * <Box>
23
+ * <Text>Background: {backgroundColor ?? "unknown"}</Text>
24
+ * <Text>Kitty: {kittyProtocol ? "yes" : "no"}</Text>
25
+ * </Box>
26
+ * );
27
+ * }
28
+ *
29
+ * render(
30
+ * <TermcapProvider>
31
+ * <App />
32
+ * </TermcapProvider>
33
+ * );
34
+ * ```
35
+ *
36
+ * @example Direct usage without React
37
+ * ```typescript
38
+ * import { detectTermcap } from "tinky-termcap";
39
+ *
40
+ * async function main() {
41
+ * const caps = await detectTermcap(process.stdin, process.stdout, 1000);
42
+ * console.log("Terminal capabilities:", caps);
43
+ * }
44
+ * ```
45
+ *
46
+ * @packageDocumentation
47
+ */
48
+ import { ReadStream, WriteStream } from "tinky";
49
+ /**
50
+ * Detected terminal capabilities information.
51
+ *
52
+ * This interface represents the result of terminal capability detection,
53
+ * containing information about various terminal features and settings.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import type { TermcapInfo } from "tinky-termcap";
58
+ *
59
+ * // Default state before detection
60
+ * const defaultCaps: TermcapInfo = {
61
+ * isReady: false,
62
+ * backgroundColor: undefined,
63
+ * terminalName: undefined,
64
+ * kittyProtocol: false,
65
+ * modifyOtherKeys: false,
66
+ * };
67
+ *
68
+ * // After detection completes
69
+ * const detectedCaps: TermcapInfo = {
70
+ * isReady: true,
71
+ * backgroundColor: "#1a1a1a",
72
+ * terminalName: "xterm(388)",
73
+ * kittyProtocol: true,
74
+ * modifyOtherKeys: true,
75
+ * };
76
+ * ```
77
+ */
78
+ export interface TermcapInfo {
79
+ /**
80
+ * Whether capability detection has completed.
81
+ *
82
+ * This is `false` during detection and becomes `true` once detection
83
+ * finishes (either successfully or via timeout).
84
+ *
85
+ * @example
86
+ * ```tsx
87
+ * const { isReady } = useTermcap();
88
+ *
89
+ * if (!isReady) {
90
+ * return <Text>Detecting terminal capabilities...</Text>;
91
+ * }
92
+ * ```
93
+ */
94
+ isReady: boolean;
95
+ /**
96
+ * Terminal background color in `#rrggbb` format.
97
+ *
98
+ * Detected via OSC 11 query. Will be `undefined` if:
99
+ * - The terminal doesn't support OSC 11
100
+ * - Detection timed out before receiving a response
101
+ * - Running in a non-TTY environment
102
+ *
103
+ * @example Adapting to light/dark themes
104
+ * ```typescript
105
+ * function isDarkBackground(color: string | undefined): boolean {
106
+ * if (!color) return true; // Assume dark if unknown
107
+ *
108
+ * const hex = color.slice(1);
109
+ * const r = parseInt(hex.slice(0, 2), 16);
110
+ * const g = parseInt(hex.slice(2, 4), 16);
111
+ * const b = parseInt(hex.slice(4, 6), 16);
112
+ *
113
+ * // Calculate relative luminance
114
+ * const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
115
+ * return luminance < 0.5;
116
+ * }
117
+ * ```
118
+ */
119
+ backgroundColor: string | undefined;
120
+ /**
121
+ * Terminal emulator name and version string.
122
+ *
123
+ * Detected via XTVERSION query. Format varies by terminal:
124
+ * - xterm: `"xterm(388)"`
125
+ * - kitty: `"kitty(0.31.0)"`
126
+ * - WezTerm: `"WezTerm 20230712-072601-f4abf8fd"`
127
+ *
128
+ * Will be `undefined` if the terminal doesn't respond to XTVERSION.
129
+ *
130
+ * @example Terminal-specific feature flags
131
+ * ```typescript
132
+ * function supportsImageProtocol(terminalName: string | undefined): boolean {
133
+ * if (!terminalName) return false;
134
+ * return terminalName.includes("kitty") ||
135
+ * terminalName.includes("WezTerm") ||
136
+ * terminalName.includes("iTerm");
137
+ * }
138
+ * ```
139
+ */
140
+ terminalName: string | undefined;
141
+ /**
142
+ * Whether Kitty keyboard protocol is supported.
143
+ *
144
+ * When `true`, the terminal supports the enhanced Kitty keyboard protocol,
145
+ * which provides:
146
+ * - Key release events
147
+ * - Separate modifier key events
148
+ * - Unicode code points for all keys
149
+ * - Disambiguation of similar key sequences
150
+ *
151
+ * @example Enabling enhanced keyboard handling
152
+ * ```typescript
153
+ * import { useTermcap } from "tinky-termcap";
154
+ *
155
+ * function useKeyboardMode() {
156
+ * const { kittyProtocol } = useTermcap();
157
+ *
158
+ * useEffect(() => {
159
+ * if (kittyProtocol) {
160
+ * // Enable Kitty keyboard protocol
161
+ * process.stdout.write("\x1b[>1u");
162
+ * return () => {
163
+ * // Disable on cleanup
164
+ * process.stdout.write("\x1b[<u");
165
+ * };
166
+ * }
167
+ * }, [kittyProtocol]);
168
+ * }
169
+ * ```
170
+ *
171
+ * @see https://sw.kovidgoyal.net/kitty/keyboard-protocol/
172
+ */
173
+ kittyProtocol: boolean;
174
+ /**
175
+ * Whether modifyOtherKeys mode is supported at level 2 or higher.
176
+ *
177
+ * When `true`, the terminal can disambiguate key sequences like:
178
+ * - `Ctrl+I` vs `Tab`
179
+ * - `Ctrl+M` vs `Enter`
180
+ * - `Ctrl+[` vs `Escape`
181
+ *
182
+ * This enables more precise keyboard handling in terminal applications.
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * const { modifyOtherKeys } = useTermcap();
187
+ *
188
+ * if (modifyOtherKeys) {
189
+ * console.log("Can distinguish Ctrl+I from Tab");
190
+ * }
191
+ * ```
192
+ *
193
+ * @see https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
194
+ */
195
+ modifyOtherKeys: boolean;
196
+ }
197
+ /**
198
+ * Default timeout for capability detection in milliseconds.
199
+ *
200
+ * This value (1000ms) provides a reasonable balance between:
201
+ * - Allowing enough time for slow terminals to respond
202
+ * - Not delaying application startup too long if terminal doesn't respond
203
+ *
204
+ * @example Custom timeout usage
205
+ * ```typescript
206
+ * import { detectTermcap, DEFAULT_DETECTION_TIMEOUT } from "tinky-termcap";
207
+ *
208
+ * // Use half the default timeout for faster startup
209
+ * const caps = await detectTermcap(stdin, stdout, DEFAULT_DETECTION_TIMEOUT / 2);
210
+ *
211
+ * // Or use a longer timeout for slow connections
212
+ * const caps = await detectTermcap(stdin, stdout, DEFAULT_DETECTION_TIMEOUT * 2);
213
+ * ```
214
+ */
215
+ export declare const DEFAULT_DETECTION_TIMEOUT = 1000;
216
+ /**
217
+ * Detect terminal capabilities by querying the terminal.
218
+ *
219
+ * This function sends escape sequence queries to the terminal and parses
220
+ * the responses to determine supported features. It detects:
221
+ * - **Background color** - via OSC 11 query
222
+ * - **Terminal name** - via XTVERSION query
223
+ * - **Kitty protocol** - enhanced keyboard support
224
+ * - **modifyOtherKeys** - key disambiguation support
225
+ *
226
+ * The function uses Device Attributes (DA) as a "sentinel" - when the DA
227
+ * response is received, detection is considered complete since terminals
228
+ * always respond to DA queries.
229
+ *
230
+ * @param stdin - Input stream to read terminal responses from.
231
+ * If not provided or not a TTY, returns default values immediately.
232
+ * @param stdout - Output stream to write queries to.
233
+ * If not provided or not a TTY, returns default values immediately.
234
+ * @param timeout - Maximum time to wait for responses in milliseconds.
235
+ * Defaults to {@link DEFAULT_DETECTION_TIMEOUT} (1000ms).
236
+ * @returns Promise resolving to detected capabilities.
237
+ *
238
+ * @remarks
239
+ * - This function should typically only be called once at app startup
240
+ * - The caller is responsible for enabling raw mode before calling
241
+ * - In non-TTY environments, returns immediately with default values
242
+ *
243
+ * @example Basic usage
244
+ * ```typescript
245
+ * import { detectTermcap } from "tinky-termcap";
246
+ *
247
+ * async function main() {
248
+ * // Enable raw mode first (required for reading responses)
249
+ * process.stdin.setRawMode(true);
250
+ *
251
+ * const caps = await detectTermcap(
252
+ * process.stdin,
253
+ * process.stdout,
254
+ * 1000
255
+ * );
256
+ *
257
+ * console.log("Detection complete:");
258
+ * console.log(" Background:", caps.backgroundColor ?? "unknown");
259
+ * console.log(" Terminal:", caps.terminalName ?? "unknown");
260
+ * console.log(" Kitty:", caps.kittyProtocol ? "yes" : "no");
261
+ * console.log(" modifyOtherKeys:", caps.modifyOtherKeys ? "yes" : "no");
262
+ * }
263
+ * ```
264
+ *
265
+ * @example With tinky hooks
266
+ * ```tsx
267
+ * import { useStdin, useStdout } from "tinky";
268
+ * import { detectTermcap } from "tinky-termcap";
269
+ *
270
+ * function DetectionComponent() {
271
+ * const { stdin, setRawMode } = useStdin();
272
+ * const { stdout } = useStdout();
273
+ * const [caps, setCaps] = useState<TermcapInfo | null>(null);
274
+ *
275
+ * useEffect(() => {
276
+ * setRawMode(true);
277
+ * detectTermcap(stdin, stdout, 1000).then(setCaps);
278
+ * }, []);
279
+ *
280
+ * if (!caps) return <Text>Detecting...</Text>;
281
+ * return <Text>Ready!</Text>;
282
+ * }
283
+ * ```
284
+ *
285
+ * @example Testing with mock streams
286
+ * ```typescript
287
+ * import { EventEmitter } from "events";
288
+ * import { ReadStream, WriteStream } from "tinky";
289
+ * import { detectTermcap } from "tinky-termcap";
290
+ *
291
+ * // Create mock streams
292
+ * const mockStdin = new EventEmitter() as ReadStream;
293
+ * (mockStdin as any).isTTY = true;
294
+ *
295
+ * const mockStdout: WriteStream = {
296
+ * isTTY: true,
297
+ * write(data: string) {
298
+ * // Simulate terminal responding to queries
299
+ * setTimeout(() => {
300
+ * mockStdin.emit("data", Buffer.from("\x1b[?62;c")); // DA response
301
+ * }, 10);
302
+ * return true;
303
+ * },
304
+ * };
305
+ *
306
+ * const caps = await detectTermcap(mockStdin, mockStdout, 100);
307
+ * ```
308
+ */
309
+ export declare function detectTermcap(stdin?: ReadStream, stdout?: WriteStream, timeout?: number): Promise<TermcapInfo>;
310
+ /**
311
+ * Reset module state for testing purposes.
312
+ *
313
+ * This function is provided for test isolation. In the current implementation,
314
+ * there is no module-level state to reset, but this function is maintained
315
+ * for API compatibility.
316
+ *
317
+ * @example
318
+ * ```typescript
319
+ * import { resetForTesting } from "tinky-termcap";
320
+ *
321
+ * beforeEach(() => {
322
+ * resetForTesting();
323
+ * });
324
+ * ```
325
+ *
326
+ * @internal
327
+ */
328
+ export declare function resetForTesting(): void;