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/LICENSE +201 -0
- package/README.ja-JP.md +285 -0
- package/README.md +287 -0
- package/README.zh-CN.md +283 -0
- package/lib/TermcapContext.d.ts +62 -0
- package/lib/TermcapContext.d.ts.map +1 -0
- package/lib/TermcapContext.js +103 -0
- package/lib/TermcapContext.js.map +1 -0
- package/lib/contexts/TermcapContext.d.ts +237 -0
- package/lib/contexts/TermcapContext.js +157 -0
- package/lib/detect.d.ts +38 -0
- package/lib/detect.d.ts.map +1 -0
- package/lib/detect.js +243 -0
- package/lib/detect.js.map +1 -0
- package/lib/hooks/use-termcap.d.ts +151 -0
- package/lib/hooks/use-termcap.js +161 -0
- package/lib/index.d.ts +85 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +92 -0
- package/lib/index.js.map +1 -0
- package/lib/utils/detect-termcap.d.ts +328 -0
- package/lib/utils/detect-termcap.js +365 -0
- package/lib/utils/detect.d.ts +33 -0
- package/lib/utils/detect.js +233 -0
- package/lib/utils/term-features.d.ts +242 -0
- package/lib/utils/term-features.js +208 -0
- package/package.json +73 -0
package/lib/detect.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2025 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
41
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
42
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
43
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
44
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
45
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
46
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
50
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
51
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
52
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
53
|
+
function step(op) {
|
|
54
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
55
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
56
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
57
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
58
|
+
switch (op[0]) {
|
|
59
|
+
case 0: case 1: t = op; break;
|
|
60
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
61
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
62
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
63
|
+
default:
|
|
64
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
65
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
66
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
67
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
68
|
+
if (t[2]) _.ops.pop();
|
|
69
|
+
_.trys.pop(); continue;
|
|
70
|
+
}
|
|
71
|
+
op = body.call(thisArg, _);
|
|
72
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
73
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
77
|
+
exports.DEFAULT_DETECTION_TIMEOUT = void 0;
|
|
78
|
+
exports.detectTerminalCapabilities = detectTerminalCapabilities;
|
|
79
|
+
exports.resetForTesting = resetForTesting;
|
|
80
|
+
var fs = __importStar(require("node:fs"));
|
|
81
|
+
// ANSI escape sequence query strings
|
|
82
|
+
var KITTY_QUERY = "\x1b[?u";
|
|
83
|
+
var OSC_11_QUERY = "\x1b]11;?\x1b\\";
|
|
84
|
+
var TERMINAL_NAME_QUERY = "\x1b[>q";
|
|
85
|
+
var DEVICE_ATTRIBUTES_QUERY = "\x1b[c";
|
|
86
|
+
var MODIFY_OTHER_KEYS_QUERY = "\x1b[>4;?m";
|
|
87
|
+
// Response regex patterns
|
|
88
|
+
// eslint-disable-next-line no-control-regex
|
|
89
|
+
var KITTY_REGEX = /\x1b\[\?(\d+)u/;
|
|
90
|
+
// eslint-disable-next-line no-control-regex
|
|
91
|
+
var TERMINAL_NAME_REGEX = /\x1bP>\|(.+?)(\x1b\\|\x07)/;
|
|
92
|
+
// eslint-disable-next-line no-control-regex
|
|
93
|
+
var DEVICE_ATTRIBUTES_REGEX = /\x1b\[\?(\d+)(;\d+)*c/;
|
|
94
|
+
// eslint-disable-next-line no-control-regex
|
|
95
|
+
var OSC_11_REGEX = /\x1b\]11;rgb:([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})(\x1b\\|\x07)?/;
|
|
96
|
+
// eslint-disable-next-line no-control-regex
|
|
97
|
+
var MODIFY_OTHER_KEYS_REGEX = /\x1b\[>4;(\d+)m/;
|
|
98
|
+
/**
|
|
99
|
+
* Parse RGB color from hex components to #rrggbb format.
|
|
100
|
+
*/
|
|
101
|
+
function parseColor(rHex, gHex, bHex) {
|
|
102
|
+
var parseComponent = function (hex) {
|
|
103
|
+
var val = parseInt(hex, 16);
|
|
104
|
+
if (hex.length === 1)
|
|
105
|
+
return (val / 15) * 255;
|
|
106
|
+
if (hex.length === 2)
|
|
107
|
+
return val;
|
|
108
|
+
if (hex.length === 3)
|
|
109
|
+
return (val / 4095) * 255;
|
|
110
|
+
if (hex.length === 4)
|
|
111
|
+
return (val / 65535) * 255;
|
|
112
|
+
return val;
|
|
113
|
+
};
|
|
114
|
+
var r = parseComponent(rHex);
|
|
115
|
+
var g = parseComponent(gHex);
|
|
116
|
+
var b = parseComponent(bHex);
|
|
117
|
+
var toHex = function (c) { return Math.round(c).toString(16).padStart(2, "0"); };
|
|
118
|
+
return "#".concat(toHex(r)).concat(toHex(g)).concat(toHex(b));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Default timeout for capability detection (in milliseconds).
|
|
122
|
+
*/
|
|
123
|
+
exports.DEFAULT_DETECTION_TIMEOUT = 1000;
|
|
124
|
+
/**
|
|
125
|
+
* Detect terminal capabilities by querying the terminal.
|
|
126
|
+
*
|
|
127
|
+
* This sends escape sequences to query terminal features and parses responses.
|
|
128
|
+
* Should only be called once at app startup.
|
|
129
|
+
*
|
|
130
|
+
* @param timeout - Maximum time to wait for responses (default: 1000ms)
|
|
131
|
+
* @returns Promise resolving to detected capabilities
|
|
132
|
+
*/
|
|
133
|
+
function detectTerminalCapabilities() {
|
|
134
|
+
return __awaiter(this, arguments, void 0, function (timeout) {
|
|
135
|
+
var defaultResult;
|
|
136
|
+
if (timeout === void 0) { timeout = exports.DEFAULT_DETECTION_TIMEOUT; }
|
|
137
|
+
return __generator(this, function (_a) {
|
|
138
|
+
defaultResult = {
|
|
139
|
+
isReady: true,
|
|
140
|
+
backgroundColor: undefined,
|
|
141
|
+
terminalName: undefined,
|
|
142
|
+
kittyProtocol: false,
|
|
143
|
+
modifyOtherKeys: false,
|
|
144
|
+
};
|
|
145
|
+
// Skip detection if not TTY
|
|
146
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
147
|
+
return [2 /*return*/, defaultResult];
|
|
148
|
+
}
|
|
149
|
+
return [2 /*return*/, new Promise(function (resolve) {
|
|
150
|
+
var originalRawMode = process.stdin.isRaw;
|
|
151
|
+
if (!originalRawMode) {
|
|
152
|
+
process.stdin.setRawMode(true);
|
|
153
|
+
}
|
|
154
|
+
var buffer = "";
|
|
155
|
+
var backgroundColor;
|
|
156
|
+
var terminalName;
|
|
157
|
+
var kittyProtocol = false;
|
|
158
|
+
var modifyOtherKeys = false;
|
|
159
|
+
var bgReceived = false;
|
|
160
|
+
var kittyReceived = false;
|
|
161
|
+
var terminalNameReceived = false;
|
|
162
|
+
var modifyOtherKeysReceived = false;
|
|
163
|
+
var deviceAttributesReceived = false;
|
|
164
|
+
var timeoutId;
|
|
165
|
+
var cleanup = function () {
|
|
166
|
+
if (timeoutId) {
|
|
167
|
+
clearTimeout(timeoutId);
|
|
168
|
+
}
|
|
169
|
+
process.stdin.removeListener("data", onData);
|
|
170
|
+
if (!originalRawMode) {
|
|
171
|
+
process.stdin.setRawMode(false);
|
|
172
|
+
}
|
|
173
|
+
resolve({
|
|
174
|
+
isReady: true,
|
|
175
|
+
backgroundColor: backgroundColor,
|
|
176
|
+
terminalName: terminalName,
|
|
177
|
+
kittyProtocol: kittyProtocol,
|
|
178
|
+
modifyOtherKeys: modifyOtherKeys,
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
timeoutId = setTimeout(cleanup, timeout);
|
|
182
|
+
var onData = function (data) {
|
|
183
|
+
buffer += data.toString();
|
|
184
|
+
// Check for background color (OSC 11 response)
|
|
185
|
+
if (!bgReceived) {
|
|
186
|
+
var match = buffer.match(OSC_11_REGEX);
|
|
187
|
+
if (match) {
|
|
188
|
+
bgReceived = true;
|
|
189
|
+
backgroundColor = parseColor(match[1], match[2], match[3]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Check for Kitty keyboard protocol support
|
|
193
|
+
if (!kittyReceived && KITTY_REGEX.test(buffer)) {
|
|
194
|
+
kittyReceived = true;
|
|
195
|
+
kittyProtocol = true;
|
|
196
|
+
}
|
|
197
|
+
// Check for modifyOtherKeys support
|
|
198
|
+
if (!modifyOtherKeysReceived) {
|
|
199
|
+
var match = buffer.match(MODIFY_OTHER_KEYS_REGEX);
|
|
200
|
+
if (match) {
|
|
201
|
+
modifyOtherKeysReceived = true;
|
|
202
|
+
var level = parseInt(match[1], 10);
|
|
203
|
+
modifyOtherKeys = level >= 2;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Check for terminal name/version
|
|
207
|
+
if (!terminalNameReceived) {
|
|
208
|
+
var match = buffer.match(TERMINAL_NAME_REGEX);
|
|
209
|
+
if (match) {
|
|
210
|
+
terminalNameReceived = true;
|
|
211
|
+
terminalName = match[1];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Device Attributes response acts as sentinel - finish when received
|
|
215
|
+
if (!deviceAttributesReceived) {
|
|
216
|
+
var match = buffer.match(DEVICE_ATTRIBUTES_REGEX);
|
|
217
|
+
if (match) {
|
|
218
|
+
deviceAttributesReceived = true;
|
|
219
|
+
cleanup();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
process.stdin.on("data", onData);
|
|
224
|
+
try {
|
|
225
|
+
fs.writeSync(process.stdout.fd, KITTY_QUERY +
|
|
226
|
+
OSC_11_QUERY +
|
|
227
|
+
TERMINAL_NAME_QUERY +
|
|
228
|
+
MODIFY_OTHER_KEYS_QUERY +
|
|
229
|
+
DEVICE_ATTRIBUTES_QUERY);
|
|
230
|
+
}
|
|
231
|
+
catch (_a) {
|
|
232
|
+
cleanup();
|
|
233
|
+
}
|
|
234
|
+
})];
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* For testing purposes only - reset module state.
|
|
240
|
+
*/
|
|
241
|
+
function resetForTesting() {
|
|
242
|
+
// No module-level state to reset in the new design
|
|
243
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview React hook for accessing terminal capability information.
|
|
3
|
+
*
|
|
4
|
+
* This module provides the `useTermcap` hook, which is the primary way to
|
|
5
|
+
* access detected terminal capabilities in React components.
|
|
6
|
+
*
|
|
7
|
+
* @example Basic usage
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { TermcapProvider, useTermcap } from "tinky-termcap";
|
|
10
|
+
*
|
|
11
|
+
* function TerminalInfo() {
|
|
12
|
+
* const { isReady, backgroundColor, terminalName } = useTermcap();
|
|
13
|
+
*
|
|
14
|
+
* if (!isReady) {
|
|
15
|
+
* return <Text>Detecting terminal capabilities...</Text>;
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* return (
|
|
19
|
+
* <Box flexDirection="column">
|
|
20
|
+
* <Text>Terminal: {terminalName ?? "Unknown"}</Text>
|
|
21
|
+
* <Text>Background: {backgroundColor ?? "Unknown"}</Text>
|
|
22
|
+
* </Box>
|
|
23
|
+
* );
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* function App() {
|
|
27
|
+
* return (
|
|
28
|
+
* <TermcapProvider>
|
|
29
|
+
* <TerminalInfo />
|
|
30
|
+
* </TermcapProvider>
|
|
31
|
+
* );
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @packageDocumentation
|
|
36
|
+
*/
|
|
37
|
+
import { type TermcapInfo } from "../utils/detect-termcap.js";
|
|
38
|
+
/**
|
|
39
|
+
* React hook to access terminal capability information.
|
|
40
|
+
*
|
|
41
|
+
* This hook provides access to the `TermcapInfo` object containing
|
|
42
|
+
* detected terminal capabilities. It must be used within a component
|
|
43
|
+
* that is a descendant of `TermcapProvider`.
|
|
44
|
+
*
|
|
45
|
+
* @returns The current terminal capability information.
|
|
46
|
+
*
|
|
47
|
+
* @throws Error if used outside of a `TermcapProvider`. The error message
|
|
48
|
+
* will be: "useTermcap must be used within a TermcapProvider"
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* - The returned `TermcapInfo.isReady` will be `false` until detection completes
|
|
52
|
+
* - Detection typically takes less than 100ms, but may take up to the timeout value
|
|
53
|
+
* - In non-TTY environments (CI, piped input), detection returns immediately with defaults
|
|
54
|
+
*
|
|
55
|
+
* @example Basic usage with loading state
|
|
56
|
+
* ```tsx
|
|
57
|
+
* import { useTermcap } from "tinky-termcap";
|
|
58
|
+
*
|
|
59
|
+
* function MyComponent() {
|
|
60
|
+
* const { isReady, backgroundColor, kittyProtocol } = useTermcap();
|
|
61
|
+
*
|
|
62
|
+
* if (!isReady) {
|
|
63
|
+
* return <Text>Detecting terminal...</Text>;
|
|
64
|
+
* }
|
|
65
|
+
*
|
|
66
|
+
* return (
|
|
67
|
+
* <Box>
|
|
68
|
+
* <Text>Background: {backgroundColor ?? "unknown"}</Text>
|
|
69
|
+
* <Text>Kitty: {kittyProtocol ? "yes" : "no"}</Text>
|
|
70
|
+
* </Box>
|
|
71
|
+
* );
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* @example Adapting UI based on terminal theme
|
|
76
|
+
* ```tsx
|
|
77
|
+
* import { useTermcap } from "tinky-termcap";
|
|
78
|
+
*
|
|
79
|
+
* function ThemedText({ children }: { children: string }) {
|
|
80
|
+
* const { backgroundColor } = useTermcap();
|
|
81
|
+
*
|
|
82
|
+
* // Determine if terminal has dark background
|
|
83
|
+
* const isDark = useMemo(() => {
|
|
84
|
+
* if (!backgroundColor) return true; // Assume dark if unknown
|
|
85
|
+
*
|
|
86
|
+
* const hex = backgroundColor.slice(1);
|
|
87
|
+
* const r = parseInt(hex.slice(0, 2), 16);
|
|
88
|
+
* const g = parseInt(hex.slice(2, 4), 16);
|
|
89
|
+
* const b = parseInt(hex.slice(4, 6), 16);
|
|
90
|
+
* const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
91
|
+
*
|
|
92
|
+
* return luminance < 0.5;
|
|
93
|
+
* }, [backgroundColor]);
|
|
94
|
+
*
|
|
95
|
+
* return (
|
|
96
|
+
* <Text color={isDark ? "white" : "black"}>
|
|
97
|
+
* {children}
|
|
98
|
+
* </Text>
|
|
99
|
+
* );
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @example Conditional keyboard handling
|
|
104
|
+
* ```tsx
|
|
105
|
+
* import { useTermcap } from "tinky-termcap";
|
|
106
|
+
* import { useInput } from "tinky";
|
|
107
|
+
*
|
|
108
|
+
* function KeyHandler() {
|
|
109
|
+
* const { kittyProtocol, modifyOtherKeys } = useTermcap();
|
|
110
|
+
*
|
|
111
|
+
* useInput((input, key) => {
|
|
112
|
+
* if (kittyProtocol || modifyOtherKeys) {
|
|
113
|
+
* // Enhanced keyboard handling available
|
|
114
|
+
* // Can distinguish Ctrl+I from Tab, etc.
|
|
115
|
+
* } else {
|
|
116
|
+
* // Fall back to basic handling
|
|
117
|
+
* }
|
|
118
|
+
* });
|
|
119
|
+
*
|
|
120
|
+
* return <Text>Press any key...</Text>;
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @example Terminal-specific features
|
|
125
|
+
* ```tsx
|
|
126
|
+
* import { useTermcap } from "tinky-termcap";
|
|
127
|
+
*
|
|
128
|
+
* function ImageViewer({ imagePath }: { imagePath: string }) {
|
|
129
|
+
* const { terminalName, kittyProtocol } = useTermcap();
|
|
130
|
+
*
|
|
131
|
+
* const supportsImages = useMemo(() => {
|
|
132
|
+
* if (!terminalName) return false;
|
|
133
|
+
* return (
|
|
134
|
+
* terminalName.includes("kitty") ||
|
|
135
|
+
* terminalName.includes("WezTerm") ||
|
|
136
|
+
* terminalName.includes("iTerm")
|
|
137
|
+
* );
|
|
138
|
+
* }, [terminalName]);
|
|
139
|
+
*
|
|
140
|
+
* if (!supportsImages) {
|
|
141
|
+
* return <Text>Image viewing not supported in this terminal</Text>;
|
|
142
|
+
* }
|
|
143
|
+
*
|
|
144
|
+
* return <KittyImage src={imagePath} />;
|
|
145
|
+
* }
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* @see {@link TermcapProvider} - The provider component that must wrap components using this hook
|
|
149
|
+
* @see {@link TermcapInfo} - The type definition for the returned capabilities object
|
|
150
|
+
*/
|
|
151
|
+
export declare function useTermcap(): TermcapInfo;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview React hook for accessing terminal capability information.
|
|
4
|
+
*
|
|
5
|
+
* This module provides the `useTermcap` hook, which is the primary way to
|
|
6
|
+
* access detected terminal capabilities in React components.
|
|
7
|
+
*
|
|
8
|
+
* @example Basic usage
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { TermcapProvider, useTermcap } from "tinky-termcap";
|
|
11
|
+
*
|
|
12
|
+
* function TerminalInfo() {
|
|
13
|
+
* const { isReady, backgroundColor, terminalName } = useTermcap();
|
|
14
|
+
*
|
|
15
|
+
* if (!isReady) {
|
|
16
|
+
* return <Text>Detecting terminal capabilities...</Text>;
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* return (
|
|
20
|
+
* <Box flexDirection="column">
|
|
21
|
+
* <Text>Terminal: {terminalName ?? "Unknown"}</Text>
|
|
22
|
+
* <Text>Background: {backgroundColor ?? "Unknown"}</Text>
|
|
23
|
+
* </Box>
|
|
24
|
+
* );
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* function App() {
|
|
28
|
+
* return (
|
|
29
|
+
* <TermcapProvider>
|
|
30
|
+
* <TerminalInfo />
|
|
31
|
+
* </TermcapProvider>
|
|
32
|
+
* );
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @packageDocumentation
|
|
37
|
+
*/
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.useTermcap = useTermcap;
|
|
40
|
+
var react_1 = require("react");
|
|
41
|
+
var TermcapContext_js_1 = require("../contexts/TermcapContext.js");
|
|
42
|
+
/**
|
|
43
|
+
* React hook to access terminal capability information.
|
|
44
|
+
*
|
|
45
|
+
* This hook provides access to the `TermcapInfo` object containing
|
|
46
|
+
* detected terminal capabilities. It must be used within a component
|
|
47
|
+
* that is a descendant of `TermcapProvider`.
|
|
48
|
+
*
|
|
49
|
+
* @returns The current terminal capability information.
|
|
50
|
+
*
|
|
51
|
+
* @throws Error if used outside of a `TermcapProvider`. The error message
|
|
52
|
+
* will be: "useTermcap must be used within a TermcapProvider"
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* - The returned `TermcapInfo.isReady` will be `false` until detection completes
|
|
56
|
+
* - Detection typically takes less than 100ms, but may take up to the timeout value
|
|
57
|
+
* - In non-TTY environments (CI, piped input), detection returns immediately with defaults
|
|
58
|
+
*
|
|
59
|
+
* @example Basic usage with loading state
|
|
60
|
+
* ```tsx
|
|
61
|
+
* import { useTermcap } from "tinky-termcap";
|
|
62
|
+
*
|
|
63
|
+
* function MyComponent() {
|
|
64
|
+
* const { isReady, backgroundColor, kittyProtocol } = useTermcap();
|
|
65
|
+
*
|
|
66
|
+
* if (!isReady) {
|
|
67
|
+
* return <Text>Detecting terminal...</Text>;
|
|
68
|
+
* }
|
|
69
|
+
*
|
|
70
|
+
* return (
|
|
71
|
+
* <Box>
|
|
72
|
+
* <Text>Background: {backgroundColor ?? "unknown"}</Text>
|
|
73
|
+
* <Text>Kitty: {kittyProtocol ? "yes" : "no"}</Text>
|
|
74
|
+
* </Box>
|
|
75
|
+
* );
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @example Adapting UI based on terminal theme
|
|
80
|
+
* ```tsx
|
|
81
|
+
* import { useTermcap } from "tinky-termcap";
|
|
82
|
+
*
|
|
83
|
+
* function ThemedText({ children }: { children: string }) {
|
|
84
|
+
* const { backgroundColor } = useTermcap();
|
|
85
|
+
*
|
|
86
|
+
* // Determine if terminal has dark background
|
|
87
|
+
* const isDark = useMemo(() => {
|
|
88
|
+
* if (!backgroundColor) return true; // Assume dark if unknown
|
|
89
|
+
*
|
|
90
|
+
* const hex = backgroundColor.slice(1);
|
|
91
|
+
* const r = parseInt(hex.slice(0, 2), 16);
|
|
92
|
+
* const g = parseInt(hex.slice(2, 4), 16);
|
|
93
|
+
* const b = parseInt(hex.slice(4, 6), 16);
|
|
94
|
+
* const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
95
|
+
*
|
|
96
|
+
* return luminance < 0.5;
|
|
97
|
+
* }, [backgroundColor]);
|
|
98
|
+
*
|
|
99
|
+
* return (
|
|
100
|
+
* <Text color={isDark ? "white" : "black"}>
|
|
101
|
+
* {children}
|
|
102
|
+
* </Text>
|
|
103
|
+
* );
|
|
104
|
+
* }
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @example Conditional keyboard handling
|
|
108
|
+
* ```tsx
|
|
109
|
+
* import { useTermcap } from "tinky-termcap";
|
|
110
|
+
* import { useInput } from "tinky";
|
|
111
|
+
*
|
|
112
|
+
* function KeyHandler() {
|
|
113
|
+
* const { kittyProtocol, modifyOtherKeys } = useTermcap();
|
|
114
|
+
*
|
|
115
|
+
* useInput((input, key) => {
|
|
116
|
+
* if (kittyProtocol || modifyOtherKeys) {
|
|
117
|
+
* // Enhanced keyboard handling available
|
|
118
|
+
* // Can distinguish Ctrl+I from Tab, etc.
|
|
119
|
+
* } else {
|
|
120
|
+
* // Fall back to basic handling
|
|
121
|
+
* }
|
|
122
|
+
* });
|
|
123
|
+
*
|
|
124
|
+
* return <Text>Press any key...</Text>;
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @example Terminal-specific features
|
|
129
|
+
* ```tsx
|
|
130
|
+
* import { useTermcap } from "tinky-termcap";
|
|
131
|
+
*
|
|
132
|
+
* function ImageViewer({ imagePath }: { imagePath: string }) {
|
|
133
|
+
* const { terminalName, kittyProtocol } = useTermcap();
|
|
134
|
+
*
|
|
135
|
+
* const supportsImages = useMemo(() => {
|
|
136
|
+
* if (!terminalName) return false;
|
|
137
|
+
* return (
|
|
138
|
+
* terminalName.includes("kitty") ||
|
|
139
|
+
* terminalName.includes("WezTerm") ||
|
|
140
|
+
* terminalName.includes("iTerm")
|
|
141
|
+
* );
|
|
142
|
+
* }, [terminalName]);
|
|
143
|
+
*
|
|
144
|
+
* if (!supportsImages) {
|
|
145
|
+
* return <Text>Image viewing not supported in this terminal</Text>;
|
|
146
|
+
* }
|
|
147
|
+
*
|
|
148
|
+
* return <KittyImage src={imagePath} />;
|
|
149
|
+
* }
|
|
150
|
+
* ```
|
|
151
|
+
*
|
|
152
|
+
* @see {@link TermcapProvider} - The provider component that must wrap components using this hook
|
|
153
|
+
* @see {@link TermcapInfo} - The type definition for the returned capabilities object
|
|
154
|
+
*/
|
|
155
|
+
function useTermcap() {
|
|
156
|
+
var context = (0, react_1.useContext)(TermcapContext_js_1.TermcapContext);
|
|
157
|
+
if (!context) {
|
|
158
|
+
throw new Error("useTermcap must be used within a TermcapProvider");
|
|
159
|
+
}
|
|
160
|
+
return context;
|
|
161
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tinky-termcap - Terminal capability detection for Tinky applications.
|
|
3
|
+
*
|
|
4
|
+
* This library provides React hooks and utilities for detecting terminal
|
|
5
|
+
* capabilities such as:
|
|
6
|
+
* - **Background color** - Detect light/dark themes via OSC 11
|
|
7
|
+
* - **Terminal name** - Identify the terminal emulator (xterm, kitty, etc.)
|
|
8
|
+
* - **Kitty keyboard protocol** - Enhanced keyboard input handling
|
|
9
|
+
* - **modifyOtherKeys** - Key sequence disambiguation (Ctrl+I vs Tab)
|
|
10
|
+
*
|
|
11
|
+
* @example Quick start
|
|
12
|
+
* ```tsx
|
|
13
|
+
* import { render } from "tinky";
|
|
14
|
+
* import { TermcapProvider, useTermcap } from "tinky-termcap";
|
|
15
|
+
*
|
|
16
|
+
* function App() {
|
|
17
|
+
* const { isReady, backgroundColor, terminalName, kittyProtocol } = useTermcap();
|
|
18
|
+
*
|
|
19
|
+
* if (!isReady) {
|
|
20
|
+
* return <Text>Detecting terminal capabilities...</Text>;
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* return (
|
|
24
|
+
* <Box flexDirection="column">
|
|
25
|
+
* <Text>Terminal: {terminalName ?? "Unknown"}</Text>
|
|
26
|
+
* <Text>Background: {backgroundColor ?? "Unknown"}</Text>
|
|
27
|
+
* <Text>Kitty Protocol: {kittyProtocol ? "Supported" : "Not supported"}</Text>
|
|
28
|
+
* </Box>
|
|
29
|
+
* );
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* render(
|
|
33
|
+
* <TermcapProvider>
|
|
34
|
+
* <App />
|
|
35
|
+
* </TermcapProvider>
|
|
36
|
+
* );
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @example Direct detection (without React)
|
|
40
|
+
* ```typescript
|
|
41
|
+
* import { detectTermcap } from "tinky-termcap";
|
|
42
|
+
*
|
|
43
|
+
* async function main() {
|
|
44
|
+
* process.stdin.setRawMode(true);
|
|
45
|
+
* const caps = await detectTermcap(process.stdin, process.stdout, 1000);
|
|
46
|
+
*
|
|
47
|
+
* console.log("Terminal:", caps.terminalName);
|
|
48
|
+
* console.log("Background:", caps.backgroundColor);
|
|
49
|
+
* console.log("Kitty:", caps.kittyProtocol);
|
|
50
|
+
* console.log("modifyOtherKeys:", caps.modifyOtherKeys);
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @packageDocumentation
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* React context provider for terminal capability detection.
|
|
58
|
+
*
|
|
59
|
+
* Wrap your application in `TermcapProvider` to enable automatic terminal
|
|
60
|
+
* capability detection. Child components can then use `useTermcap()` to
|
|
61
|
+
* access the detected capabilities.
|
|
62
|
+
*
|
|
63
|
+
* @see {@link TermcapProviderProps} for configuration options
|
|
64
|
+
*/
|
|
65
|
+
export { TermcapProvider, type TermcapInfo, type TermcapProviderProps, } from "./contexts/TermcapContext.js";
|
|
66
|
+
/**
|
|
67
|
+
* React hook for accessing terminal capabilities.
|
|
68
|
+
*
|
|
69
|
+
* Must be used within a `TermcapProvider`. Returns a `TermcapInfo` object
|
|
70
|
+
* containing the detected terminal capabilities.
|
|
71
|
+
*
|
|
72
|
+
* @see {@link TermcapInfo} for the shape of returned data
|
|
73
|
+
*/
|
|
74
|
+
export { useTermcap } from "./hooks/use-termcap.js";
|
|
75
|
+
/**
|
|
76
|
+
* Low-level terminal capability detection function.
|
|
77
|
+
*
|
|
78
|
+
* Use this directly when you need capability detection outside of React,
|
|
79
|
+
* or when you need more control over the detection process.
|
|
80
|
+
*/
|
|
81
|
+
export { detectTermcap } from "./utils/detect-termcap.js";
|
|
82
|
+
/**
|
|
83
|
+
* Re-export additional types and constants for advanced usage.
|
|
84
|
+
*/
|
|
85
|
+
export { DEFAULT_DETECTION_TIMEOUT } from "./utils/detect-termcap.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,eAAe,EACf,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,eAAe,GAChB,MAAM,aAAa,CAAC"}
|