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/README.md ADDED
@@ -0,0 +1,287 @@
1
+ # tinky-termcap
2
+
3
+ Terminal capability detection library for [Tinky](https://github.com/ByteLandTechnology/tinky) applications. Provides React hooks and utilities for detecting terminal features like background color, Kitty keyboard protocol support, and modifyOtherKeys mode.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Background Color Detection** - Detect terminal theme (light/dark) via OSC 11
8
+ - 📝 **Terminal Identification** - Get terminal name and version (xterm, kitty, WezTerm, etc.)
9
+ - ⌨️ **Kitty Keyboard Protocol** - Detect enhanced keyboard input support
10
+ - 🔧 **modifyOtherKeys** - Detect key sequence disambiguation (Ctrl+I vs Tab)
11
+ - ⚛️ **React Integration** - Seamless hooks and context provider for Tinky apps
12
+
13
+ ## Attribution
14
+
15
+ This project is based on the terminal capability detection implementation from [gemini-cli](https://github.com/google-gemini/gemini-cli).
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install tinky-termcap
21
+ ```
22
+
23
+ **Peer Dependencies:**
24
+
25
+ - `tinky` >= 1.0.0
26
+
27
+ ## Quick Start
28
+
29
+ ### Using the Provider and Hook
30
+
31
+ Wrap your application in `TermcapProvider` and use the `useTermcap` hook to access terminal capabilities.
32
+
33
+ ```tsx
34
+ import { render, Box, Text } from "tinky";
35
+ import { TermcapProvider, useTermcap } from "tinky-termcap";
36
+
37
+ function App() {
38
+ const { isReady, backgroundColor, terminalName, kittyProtocol } =
39
+ useTermcap();
40
+
41
+ if (!isReady) {
42
+ return <Text>Detecting terminal capabilities...</Text>;
43
+ }
44
+
45
+ return (
46
+ <Box flexDirection="column">
47
+ <Text>Terminal: {terminalName ?? "Unknown"}</Text>
48
+ <Text>Background: {backgroundColor ?? "Unknown"}</Text>
49
+ <Text>
50
+ Kitty Protocol: {kittyProtocol ? "Supported" : "Not supported"}
51
+ </Text>
52
+ </Box>
53
+ );
54
+ }
55
+
56
+ render(
57
+ <TermcapProvider>
58
+ <App />
59
+ </TermcapProvider>,
60
+ );
61
+ ```
62
+
63
+ ### Adapting to Terminal Theme
64
+
65
+ ```tsx
66
+ import { useTermcap } from "tinky-termcap";
67
+ import { useMemo } from "react";
68
+
69
+ function ThemedComponent() {
70
+ const { backgroundColor } = useTermcap();
71
+
72
+ const isDarkTheme = useMemo(() => {
73
+ if (!backgroundColor) return true; // Assume dark if unknown
74
+
75
+ const hex = backgroundColor.slice(1);
76
+ const r = parseInt(hex.slice(0, 2), 16);
77
+ const g = parseInt(hex.slice(2, 4), 16);
78
+ const b = parseInt(hex.slice(4, 6), 16);
79
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
80
+
81
+ return luminance < 0.5;
82
+ }, [backgroundColor]);
83
+
84
+ return (
85
+ <Box borderStyle="round" borderColor={isDarkTheme ? "white" : "black"}>
86
+ <Text color={isDarkTheme ? "cyan" : "blue"}>Adaptive content</Text>
87
+ </Box>
88
+ );
89
+ }
90
+ ```
91
+
92
+ ### Direct Usage (Without React)
93
+
94
+ ```typescript
95
+ import { detectTermcap } from "tinky-termcap";
96
+
97
+ async function main() {
98
+ // Enable raw mode for reading terminal responses
99
+ process.stdin.setRawMode(true);
100
+
101
+ const caps = await detectTermcap(process.stdin, process.stdout, 1000);
102
+
103
+ console.log("Detection complete:");
104
+ console.log(" Terminal:", caps.terminalName ?? "Unknown");
105
+ console.log(" Background:", caps.backgroundColor ?? "Unknown");
106
+ console.log(" Kitty Protocol:", caps.kittyProtocol ? "Yes" : "No");
107
+ console.log(" modifyOtherKeys:", caps.modifyOtherKeys ? "Yes" : "No");
108
+
109
+ process.stdin.setRawMode(false);
110
+ process.exit(0);
111
+ }
112
+
113
+ main();
114
+ ```
115
+
116
+ ## API Reference
117
+
118
+ ### `TermcapProvider`
119
+
120
+ React context provider that performs terminal capability detection.
121
+
122
+ #### Props
123
+
124
+ | Prop | Type | Default | Description |
125
+ | --------------------- | ----------------- | ------- | ---------------------------------------------------- |
126
+ | `children` | `React.ReactNode` | - | Child components |
127
+ | `timeout` | `number` | `1000` | Detection timeout in milliseconds |
128
+ | `initialCapabilities` | `TermcapInfo` | - | Skip detection and use provided values (for testing) |
129
+
130
+ #### Example with Custom Timeout
131
+
132
+ ```tsx
133
+ <TermcapProvider timeout={500}>
134
+ <App />
135
+ </TermcapProvider>
136
+ ```
137
+
138
+ #### Example for Testing
139
+
140
+ ```tsx
141
+ <TermcapProvider
142
+ initialCapabilities={{
143
+ isReady: true,
144
+ backgroundColor: "#1a1a1a",
145
+ terminalName: "xterm-256color",
146
+ kittyProtocol: true,
147
+ modifyOtherKeys: true,
148
+ }}
149
+ >
150
+ <ComponentUnderTest />
151
+ </TermcapProvider>
152
+ ```
153
+
154
+ ### `useTermcap()`
155
+
156
+ React hook to access terminal capabilities. Must be used within `TermcapProvider`.
157
+
158
+ #### Returns: `TermcapInfo`
159
+
160
+ | Property | Type | Description |
161
+ | ----------------- | --------------------- | ------------------------------------ |
162
+ | `isReady` | `boolean` | Whether detection has completed |
163
+ | `backgroundColor` | `string \| undefined` | Background color in `#rrggbb` format |
164
+ | `terminalName` | `string \| undefined` | Terminal name/version string |
165
+ | `kittyProtocol` | `boolean` | Kitty keyboard protocol support |
166
+ | `modifyOtherKeys` | `boolean` | modifyOtherKeys (level ≥ 2) support |
167
+
168
+ #### Example
169
+
170
+ ```tsx
171
+ function MyComponent() {
172
+ const { isReady, backgroundColor, kittyProtocol, modifyOtherKeys } =
173
+ useTermcap();
174
+
175
+ if (!isReady) {
176
+ return <Text>Loading...</Text>;
177
+ }
178
+
179
+ return (
180
+ <Box flexDirection="column">
181
+ <Text>Background: {backgroundColor ?? "unknown"}</Text>
182
+ <Text>
183
+ Enhanced keyboard: {kittyProtocol || modifyOtherKeys ? "✓" : "✗"}
184
+ </Text>
185
+ </Box>
186
+ );
187
+ }
188
+ ```
189
+
190
+ ### `detectTermcap()`
191
+
192
+ Low-level function for direct terminal capability detection.
193
+
194
+ ```typescript
195
+ function detectTermcap(
196
+ stdin?: IOReadStream,
197
+ stdout?: IOWriteStream,
198
+ timeout?: number,
199
+ ): Promise<TermcapInfo>;
200
+ ```
201
+
202
+ #### Parameters
203
+
204
+ | Parameter | Type | Description |
205
+ | --------- | ------------- | -------------------------------------- |
206
+ | `stdin` | `ReadStream` | Input stream (e.g., `process.stdin`) |
207
+ | `stdout` | `WriteStream` | Output stream (e.g., `process.stdout`) |
208
+ | `timeout` | `number` | Detection timeout in milliseconds |
209
+
210
+ #### Example
211
+
212
+ ```typescript
213
+ import { detectTermcap } from "tinky-termcap";
214
+
215
+ const caps = await detectTermcap(process.stdin, process.stdout, 1000);
216
+ ```
217
+
218
+ ### Terminal Features
219
+
220
+ The library exports feature definitions for advanced use cases:
221
+
222
+ ```typescript
223
+ import {
224
+ KittyFeature,
225
+ Osc11Feature,
226
+ TerminalNameFeature,
227
+ DeviceAttributesFeature,
228
+ ModifyOtherKeysFeature,
229
+ type TermFeature,
230
+ } from "tinky-termcap/utils/term-features";
231
+
232
+ // Each feature has:
233
+ // - query: ANSI escape sequence to send
234
+ // - responseRegex: Pattern to match the response
235
+
236
+ // Example: Custom detection
237
+ process.stdout.write(KittyFeature.query);
238
+ // Listen for response matching KittyFeature.responseRegex
239
+ ```
240
+
241
+ ## How It Works
242
+
243
+ 1. **On Mount**: `TermcapProvider` enables raw mode and sends detection queries
244
+ 2. **Query Sequence**: Sends escape sequences for each feature:
245
+ - Kitty keyboard protocol query
246
+ - OSC 11 (background color)
247
+ - XTVERSION (terminal name)
248
+ - modifyOtherKeys query
249
+ - Device Attributes (sentinel)
250
+ 3. **Response Parsing**: Parses terminal responses as they arrive
251
+ 4. **Completion**: Detection completes when either:
252
+ - Device Attributes response received (indicates all responses sent)
253
+ - Timeout reached
254
+
255
+ ## Detected Terminals
256
+
257
+ Tested with:
258
+
259
+ - **xterm** - Full OSC 11 and XTVERSION support
260
+ - **kitty** - Kitty protocol, XTVERSION, OSC 11
261
+ - **WezTerm** - Full feature support
262
+ - **iTerm2** - OSC 11, XTVERSION
263
+ - **Alacritty** - OSC 11
264
+ - **macOS Terminal** - Basic support
265
+ - **VS Code Terminal** - OSC 11
266
+
267
+ ## Development
268
+
269
+ ```bash
270
+ # Install dependencies
271
+ npm install
272
+
273
+ # Build
274
+ npm run build
275
+
276
+ # Run tests
277
+ bun test
278
+
279
+ # Generate docs
280
+ npm run docs
281
+ ```
282
+
283
+ ## License
284
+
285
+ Apache-2.0
286
+
287
+ See [LICENSE](./LICENSE) for details.
@@ -0,0 +1,283 @@
1
+ # tinky-termcap
2
+
3
+ [Tinky](https://github.com/ByteLandTechnology/tinky) 应用程序的终端能力检测库。提供 React 钩子和工具函数,用于检测终端特性,如背景颜色、Kitty 键盘协议支持和 modifyOtherKeys 模式。
4
+
5
+ ## 特性
6
+
7
+ - 🎨 **背景颜色检测** - 通过 OSC 11 检测终端主题(亮色/暗色)
8
+ - 📝 **终端识别** - 获取终端名称和版本(xterm、kitty、WezTerm 等)
9
+ - ⌨️ **Kitty 键盘协议** - 检测增强键盘输入支持
10
+ - 🔧 **modifyOtherKeys** - 检测按键序列区分(Ctrl+I 与 Tab)
11
+ - ⚛️ **React 集成** - 为 Tinky 应用提供无缝的钩子和上下文提供者
12
+
13
+ ## 致谢
14
+
15
+ 本项目基于 [gemini-cli](https://github.com/google-gemini/gemini-cli) 的终端能力检测实现。
16
+
17
+ ## 安装
18
+
19
+ ```bash
20
+ npm install tinky-termcap
21
+ ```
22
+
23
+ **对等依赖:**
24
+
25
+ - `tinky` >= 1.0.0
26
+
27
+ ## 快速开始
28
+
29
+ ### 使用 Provider 和 Hook
30
+
31
+ 用 `TermcapProvider` 包装您的应用程序,并使用 `useTermcap` 钩子访问终端能力。
32
+
33
+ ```tsx
34
+ import { render, Box, Text } from "tinky";
35
+ import { TermcapProvider, useTermcap } from "tinky-termcap";
36
+
37
+ function App() {
38
+ const { isReady, backgroundColor, terminalName, kittyProtocol } =
39
+ useTermcap();
40
+
41
+ if (!isReady) {
42
+ return <Text>正在检测终端能力...</Text>;
43
+ }
44
+
45
+ return (
46
+ <Box flexDirection="column">
47
+ <Text>终端:{terminalName ?? "未知"}</Text>
48
+ <Text>背景色:{backgroundColor ?? "未知"}</Text>
49
+ <Text>Kitty 协议:{kittyProtocol ? "支持" : "不支持"}</Text>
50
+ </Box>
51
+ );
52
+ }
53
+
54
+ render(
55
+ <TermcapProvider>
56
+ <App />
57
+ </TermcapProvider>,
58
+ );
59
+ ```
60
+
61
+ ### 适应终端主题
62
+
63
+ ```tsx
64
+ import { useTermcap } from "tinky-termcap";
65
+ import { useMemo } from "react";
66
+
67
+ function ThemedComponent() {
68
+ const { backgroundColor } = useTermcap();
69
+
70
+ const isDarkTheme = useMemo(() => {
71
+ if (!backgroundColor) return true; // 未知时假设为暗色
72
+
73
+ const hex = backgroundColor.slice(1);
74
+ const r = parseInt(hex.slice(0, 2), 16);
75
+ const g = parseInt(hex.slice(2, 4), 16);
76
+ const b = parseInt(hex.slice(4, 6), 16);
77
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
78
+
79
+ return luminance < 0.5;
80
+ }, [backgroundColor]);
81
+
82
+ return (
83
+ <Box borderStyle="round" borderColor={isDarkTheme ? "white" : "black"}>
84
+ <Text color={isDarkTheme ? "cyan" : "blue"}>自适应内容</Text>
85
+ </Box>
86
+ );
87
+ }
88
+ ```
89
+
90
+ ### 直接使用(不使用 React)
91
+
92
+ ```typescript
93
+ import { detectTermcap } from "tinky-termcap";
94
+
95
+ async function main() {
96
+ // 启用原始模式以读取终端响应
97
+ process.stdin.setRawMode(true);
98
+
99
+ const caps = await detectTermcap(process.stdin, process.stdout, 1000);
100
+
101
+ console.log("检测完成:");
102
+ console.log(" 终端:", caps.terminalName ?? "未知");
103
+ console.log(" 背景色:", caps.backgroundColor ?? "未知");
104
+ console.log(" Kitty 协议:", caps.kittyProtocol ? "是" : "否");
105
+ console.log(" modifyOtherKeys:", caps.modifyOtherKeys ? "是" : "否");
106
+
107
+ process.stdin.setRawMode(false);
108
+ process.exit(0);
109
+ }
110
+
111
+ main();
112
+ ```
113
+
114
+ ## API 参考
115
+
116
+ ### `TermcapProvider`
117
+
118
+ 执行终端能力检测的 React 上下文提供者。
119
+
120
+ #### 属性
121
+
122
+ | 属性 | 类型 | 默认值 | 描述 |
123
+ | --------------------- | ----------------- | ------ | ---------------------------------- |
124
+ | `children` | `React.ReactNode` | - | 子组件 |
125
+ | `timeout` | `number` | `1000` | 检测超时时间(毫秒) |
126
+ | `initialCapabilities` | `TermcapInfo` | - | 跳过检测并使用提供的值(用于测试) |
127
+
128
+ #### 自定义超时示例
129
+
130
+ ```tsx
131
+ <TermcapProvider timeout={500}>
132
+ <App />
133
+ </TermcapProvider>
134
+ ```
135
+
136
+ #### 测试示例
137
+
138
+ ```tsx
139
+ <TermcapProvider
140
+ initialCapabilities={{
141
+ isReady: true,
142
+ backgroundColor: "#1a1a1a",
143
+ terminalName: "xterm-256color",
144
+ kittyProtocol: true,
145
+ modifyOtherKeys: true,
146
+ }}
147
+ >
148
+ <ComponentUnderTest />
149
+ </TermcapProvider>
150
+ ```
151
+
152
+ ### `useTermcap()`
153
+
154
+ 访问终端能力的 React 钩子。必须在 `TermcapProvider` 内部使用。
155
+
156
+ #### 返回值:`TermcapInfo`
157
+
158
+ | 属性 | 类型 | 描述 |
159
+ | ----------------- | --------------------- | ------------------------------- |
160
+ | `isReady` | `boolean` | 检测是否已完成 |
161
+ | `backgroundColor` | `string \| undefined` | `#rrggbb` 格式的背景颜色 |
162
+ | `terminalName` | `string \| undefined` | 终端名称/版本字符串 |
163
+ | `kittyProtocol` | `boolean` | Kitty 键盘协议支持 |
164
+ | `modifyOtherKeys` | `boolean` | modifyOtherKeys(级别 ≥ 2)支持 |
165
+
166
+ #### 示例
167
+
168
+ ```tsx
169
+ function MyComponent() {
170
+ const { isReady, backgroundColor, kittyProtocol, modifyOtherKeys } =
171
+ useTermcap();
172
+
173
+ if (!isReady) {
174
+ return <Text>加载中...</Text>;
175
+ }
176
+
177
+ return (
178
+ <Box flexDirection="column">
179
+ <Text>背景色:{backgroundColor ?? "未知"}</Text>
180
+ <Text>增强键盘:{kittyProtocol || modifyOtherKeys ? "✓" : "✗"}</Text>
181
+ </Box>
182
+ );
183
+ }
184
+ ```
185
+
186
+ ### `detectTermcap()`
187
+
188
+ 用于直接终端能力检测的底层函数。
189
+
190
+ ```typescript
191
+ function detectTermcap(
192
+ stdin?: ReadStream,
193
+ stdout?: WriteStream,
194
+ timeout?: number,
195
+ ): Promise<TermcapInfo>;
196
+ ```
197
+
198
+ #### 参数
199
+
200
+ | 参数 | 类型 | 描述 |
201
+ | --------- | ------------- | ------------------------------- |
202
+ | `stdin` | `ReadStream` | 输入流(例如 `process.stdin`) |
203
+ | `stdout` | `WriteStream` | 输出流(例如 `process.stdout`) |
204
+ | `timeout` | `number` | 检测超时时间(毫秒) |
205
+
206
+ #### 示例
207
+
208
+ ```typescript
209
+ import { detectTermcap } from "tinky-termcap";
210
+
211
+ const caps = await detectTermcap(process.stdin, process.stdout, 1000);
212
+ ```
213
+
214
+ ### 终端特性
215
+
216
+ 该库为高级用例导出特性定义:
217
+
218
+ ```typescript
219
+ import {
220
+ KittyFeature,
221
+ Osc11Feature,
222
+ TerminalNameFeature,
223
+ DeviceAttributesFeature,
224
+ ModifyOtherKeysFeature,
225
+ type TermFeature,
226
+ } from "tinky-termcap/utils/term-features";
227
+
228
+ // 每个特性包含:
229
+ // - query:要发送的 ANSI 转义序列
230
+ // - responseRegex:匹配响应的模式
231
+
232
+ // 示例:自定义检测
233
+ process.stdout.write(KittyFeature.query);
234
+ // 监听匹配 KittyFeature.responseRegex 的响应
235
+ ```
236
+
237
+ ## 工作原理
238
+
239
+ 1. **挂载时**:`TermcapProvider` 启用原始模式并发送检测查询
240
+ 2. **查询序列**:为每个特性发送转义序列:
241
+ - Kitty 键盘协议查询
242
+ - OSC 11(背景颜色)
243
+ - XTVERSION(终端名称)
244
+ - modifyOtherKeys 查询
245
+ - Device Attributes(哨兵)
246
+ 3. **响应解析**:在响应到达时解析终端响应
247
+ 4. **完成**:检测在以下情况完成:
248
+ - 收到 Device Attributes 响应(表示所有响应已发送)
249
+ - 超时到达
250
+
251
+ ## 已测试终端
252
+
253
+ 已测试:
254
+
255
+ - **xterm** - 完整支持 OSC 11 和 XTVERSION
256
+ - **kitty** - Kitty 协议、XTVERSION、OSC 11
257
+ - **WezTerm** - 完整特性支持
258
+ - **iTerm2** - OSC 11、XTVERSION
259
+ - **Alacritty** - OSC 11
260
+ - **macOS 终端** - 基本支持
261
+ - **VS Code 终端** - OSC 11
262
+
263
+ ## 开发
264
+
265
+ ```bash
266
+ # 安装依赖
267
+ npm install
268
+
269
+ # 构建
270
+ npm run build
271
+
272
+ # 运行测试
273
+ bun test
274
+
275
+ # 生成文档
276
+ npm run docs
277
+ ```
278
+
279
+ ## 许可证
280
+
281
+ Apache-2.0
282
+
283
+ 详见 [LICENSE](./LICENSE)。
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type React from "react";
7
+ import { type TermcapInfo } from "./detect.js";
8
+ /**
9
+ * Hook to access terminal capability information.
10
+ *
11
+ * Must be used within a `TermcapProvider`.
12
+ *
13
+ * @returns Terminal capability information
14
+ * @throws If used outside of TermcapProvider
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * function MyComponent() {
19
+ * const { isReady, backgroundColor, kittyProtocol } = useTermcap();
20
+ *
21
+ * if (!isReady) return <Text>Detecting terminal...</Text>;
22
+ *
23
+ * return (
24
+ * <Box>
25
+ * <Text>Background: {backgroundColor ?? 'unknown'}</Text>
26
+ * <Text>Kitty: {kittyProtocol ? 'yes' : 'no'}</Text>
27
+ * </Box>
28
+ * );
29
+ * }
30
+ * ```
31
+ */
32
+ export declare function useTermcap(): TermcapInfo;
33
+ /**
34
+ * Props for TermcapProvider.
35
+ */
36
+ export interface TermcapProviderProps {
37
+ /** Child components */
38
+ children: React.ReactNode;
39
+ /** Detection timeout in milliseconds (default: 1000) */
40
+ timeout?: number;
41
+ /** Skip detection and use provided capabilities (for testing) */
42
+ initialCapabilities?: TermcapInfo;
43
+ }
44
+ /**
45
+ * Provider component that detects and provides terminal capabilities.
46
+ *
47
+ * Performs capability detection on mount and provides results to children
48
+ * via the `useTermcap` hook.
49
+ *
50
+ * @example
51
+ * ```tsx
52
+ * function App() {
53
+ * return (
54
+ * <TermcapProvider>
55
+ * <MyTerminalApp />
56
+ * </TermcapProvider>
57
+ * );
58
+ * }
59
+ * ```
60
+ */
61
+ export declare function TermcapProvider({ children, timeout, initialCapabilities, }: TermcapProviderProps): React.ReactElement;
62
+ export type { TermcapInfo };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TermcapContext.d.ts","sourceRoot":"","sources":["../src/TermcapContext.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAC;AAerB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,UAAU,IAAI,WAAW,CAMxC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uBAAuB;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,mBAAmB,CAAC,EAAE,WAAW,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,OAAmC,EACnC,mBAAmB,GACpB,EAAE,oBAAoB,GAAG,KAAK,CAAC,YAAY,CA6B3C;AAED,YAAY,EAAE,WAAW,EAAE,CAAC"}