tinky-keypress 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.ja-JP.md +112 -0
- package/README.md +112 -0
- package/README.zh-CN.md +112 -0
- package/lib/contexts/KeypressContext.d.ts +48 -0
- package/lib/contexts/KeypressContext.js +714 -0
- package/lib/hooks/use-keypress.d.ts +12 -0
- package/lib/hooks/use-keypress.js +25 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +20 -0
- package/package.json +72 -0
package/README.ja-JP.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# tinky-keypress
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | [简体中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
> [Tinky](https://github.com/ByteLandTechnology/tinky) アプリケーションのための堅牢なキープレス処理と入力解析。
|
|
6
|
+
|
|
7
|
+
このパッケージは、ターミナルユーザーインターフェースにおける標準入力 (stdin) を管理するための専用コンテキストとフックシステムを提供します。特殊キー、修飾キー、モダンなターミナルプロトコルを含む、生の ANSI エスケープシーケンス解析の複雑さを処理します。
|
|
8
|
+
|
|
9
|
+
## 特徴
|
|
10
|
+
|
|
11
|
+
- **簡単な統合**: ドロップインで使用できるコンテキストプロバイダー (`KeypressProvider`) とフック (`useKeypress`)。
|
|
12
|
+
- **包括的な解析**: 標準キー、ファンクションキー (F1-F12)、ナビゲーションキーなどをサポート。
|
|
13
|
+
- **修飾キーのサポート**: Control、Alt/Meta、Shift 修飾キーをサポート。
|
|
14
|
+
- **高度なプロトコル**: Kitty キーボードプロトコルのオプションサポート。
|
|
15
|
+
- **マウスとクリップボード**: マウスイベントのフィルタリングと OSC 52 ペーストシーケンスのサポートを内蔵。
|
|
16
|
+
- **型安全性**: TypeScript で記述されており、完全な型定義を提供します。
|
|
17
|
+
|
|
18
|
+
## インストール
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install tinky-keypress
|
|
22
|
+
# または
|
|
23
|
+
yarn add tinky-keypress
|
|
24
|
+
# または
|
|
25
|
+
pnpm add tinky-keypress
|
|
26
|
+
# または
|
|
27
|
+
bun add tinky-keypress
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 使用方法
|
|
31
|
+
|
|
32
|
+
アプリケーションのルートを `KeypressProvider` でラップし、任意の子コンポーネントで `useKeypress` を使用します。
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import React, { useState } from "react";
|
|
36
|
+
import { render, Text } from "tinky";
|
|
37
|
+
import { KeypressProvider, useKeypress } from "tinky-keypress";
|
|
38
|
+
|
|
39
|
+
function App() {
|
|
40
|
+
const [lastEvent, setLastEvent] = useState<string>("キーを押してください...");
|
|
41
|
+
|
|
42
|
+
useKeypress(
|
|
43
|
+
(key) => {
|
|
44
|
+
const parts = [key.name];
|
|
45
|
+
if (key.ctrl) parts.push("Ctrl");
|
|
46
|
+
if (key.meta) parts.push("Alt");
|
|
47
|
+
if (key.shift) parts.push("Shift");
|
|
48
|
+
|
|
49
|
+
setLastEvent(
|
|
50
|
+
`検出: ${parts.join("+")} (シーケンス: ${JSON.stringify(key.sequence)})`,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (key.name === "q" && !key.ctrl) {
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ isActive: true },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return <Text>{lastEvent}</Text>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const instance = render(
|
|
64
|
+
<KeypressProvider>
|
|
65
|
+
<App />
|
|
66
|
+
</KeypressProvider>,
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## API
|
|
71
|
+
|
|
72
|
+
### `<KeypressProvider />`
|
|
73
|
+
|
|
74
|
+
`process.stdin` をリッスンするトップレベルのプロバイダー。
|
|
75
|
+
|
|
76
|
+
| プロパティ | 型 | デフォルト値 | 説明 |
|
|
77
|
+
| --------------- | ----------- | ------------ | ---------------------------------------------------------------------------------------- |
|
|
78
|
+
| `children` | `ReactNode` | N/A | キープレスフックを使用できる子コンポーネント。 |
|
|
79
|
+
| `kittyProtocol` | `boolean` | `false` | Kitty キーボードプロトコルのサポートを有効にします(ターミナルがサポートしている場合)。 |
|
|
80
|
+
|
|
81
|
+
### `useKeypress(handler, options)`
|
|
82
|
+
|
|
83
|
+
キープレスイベントを購読するためのフック。
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
useKeypress(
|
|
87
|
+
handler: (key: Key) => void,
|
|
88
|
+
options: { isActive: boolean }
|
|
89
|
+
): void
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- **handler**: キープレスが発生したときに呼び出される関数。
|
|
93
|
+
- **options.isActive**: リスナーを有効にするかどうか(フォーカス管理に便利です)。
|
|
94
|
+
|
|
95
|
+
### `Key` インターフェース
|
|
96
|
+
|
|
97
|
+
ハンドラーに渡されるイベントオブジェクト:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
interface Key {
|
|
101
|
+
name: string; // キーの名前 (例: 'a', 'enter', 'escape', 'up')
|
|
102
|
+
ctrl: boolean; // Control キーが押されているかどうか
|
|
103
|
+
meta: boolean; // Alt/Meta キーが押されているかどうか
|
|
104
|
+
shift: boolean; // Shift キーが押されているかどうか
|
|
105
|
+
insertable: boolean; // キーが表示可能な文字を生成するかどうか
|
|
106
|
+
sequence: string; // 受信した生の ANSI シーケンス
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## ライセンス
|
|
111
|
+
|
|
112
|
+
Apache-2.0
|
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# tinky-keypress
|
|
2
|
+
|
|
3
|
+
[简体中文](./README.zh-CN.md) | [日本語](./README.ja-JP.md)
|
|
4
|
+
|
|
5
|
+
> Robust keypress handling and input parsing for [Tinky](https://github.com/ByteLandTechnology/tinky) applications.
|
|
6
|
+
|
|
7
|
+
This package provides a dedicated context and hooks system for managing standard input (stdin) in terminal user interfaces. It handles the complexity of parsing raw ANSI escape sequences, including special keys, modifiers, and modern terminal protocols.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Easy Integration**: Drop-in context provider (`KeypressProvider`) and hook (`useKeypress`).
|
|
12
|
+
- **Comprehensive Parsing**: Supports standard keys, function keys (F1-F12), navigation keys, and more.
|
|
13
|
+
- **Modifier Support**: Handles Control, Alt/Meta, and Shift modifiers.
|
|
14
|
+
- **Advanced Protocols**: Optional support for the Kitty Keyboard Protocol.
|
|
15
|
+
- **Mouse & Clipboard**: Built-in filtering for mouse events and support for OSC 52 paste sequences.
|
|
16
|
+
- **Type-Safe**: Written in TypeScript with complete type definitions.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install tinky-keypress
|
|
22
|
+
# or
|
|
23
|
+
yarn add tinky-keypress
|
|
24
|
+
# or
|
|
25
|
+
pnpm add tinky-keypress
|
|
26
|
+
# or
|
|
27
|
+
bun add tinky-keypress
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
Wrap your application root with `KeypressProvider` and use `useKeypress` in any child component.
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import React, { useState } from "react";
|
|
36
|
+
import { render, Text } from "tinky";
|
|
37
|
+
import { KeypressProvider, useKeypress } from "tinky-keypress";
|
|
38
|
+
|
|
39
|
+
function App() {
|
|
40
|
+
const [lastEvent, setLastEvent] = useState<string>("Press any key...");
|
|
41
|
+
|
|
42
|
+
useKeypress(
|
|
43
|
+
(key) => {
|
|
44
|
+
const parts = [key.name];
|
|
45
|
+
if (key.ctrl) parts.push("Ctrl");
|
|
46
|
+
if (key.meta) parts.push("Alt");
|
|
47
|
+
if (key.shift) parts.push("Shift");
|
|
48
|
+
|
|
49
|
+
setLastEvent(
|
|
50
|
+
`Detected: ${parts.join("+")} (Sequence: ${JSON.stringify(key.sequence)})`,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (key.name === "q" && !key.ctrl) {
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ isActive: true },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return <Text>{lastEvent}</Text>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const instance = render(
|
|
64
|
+
<KeypressProvider>
|
|
65
|
+
<App />
|
|
66
|
+
</KeypressProvider>,
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## API
|
|
71
|
+
|
|
72
|
+
### `<KeypressProvider />`
|
|
73
|
+
|
|
74
|
+
The top-level provider that listens to `process.stdin`.
|
|
75
|
+
|
|
76
|
+
| Prop | Type | Default | Description |
|
|
77
|
+
| --------------- | ----------- | ------- | ------------------------------------------------------------------------------ |
|
|
78
|
+
| `children` | `ReactNode` | N/A | Child components that can use keypress hooks. |
|
|
79
|
+
| `kittyProtocol` | `boolean` | `false` | Enable support for the Kitty keyboard protocol (if supported by the terminal). |
|
|
80
|
+
|
|
81
|
+
### `useKeypress(handler, options)`
|
|
82
|
+
|
|
83
|
+
Hook to subscribe to keypress events.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
useKeypress(
|
|
87
|
+
handler: (key: Key) => void,
|
|
88
|
+
options: { isActive: boolean }
|
|
89
|
+
): void
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- **handler**: Function called whenever a keypress occurs.
|
|
93
|
+
- **options.isActive**: Boolean to enable or disable the listener (useful for focus management).
|
|
94
|
+
|
|
95
|
+
### `Key` Interface
|
|
96
|
+
|
|
97
|
+
The event object passed to the handler:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
interface Key {
|
|
101
|
+
name: string; // The name of the key (e.g., 'a', 'enter', 'escape', 'up')
|
|
102
|
+
ctrl: boolean; // Whether the Control key is pressed
|
|
103
|
+
meta: boolean; // Whether the Alt/Meta key is pressed
|
|
104
|
+
shift: boolean; // Whether the Shift key is pressed
|
|
105
|
+
insertable: boolean; // Whether the key produces a printable character
|
|
106
|
+
sequence: string; // The raw ANSI sequence received
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
Apache-2.0
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# tinky-keypress
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | [日本語](./README.ja-JP.md)
|
|
4
|
+
|
|
5
|
+
> 为 [Tinky](https://github.com/ByteLandTechnology/tinky) 应用提供强大的按键处理和输入解析功能。
|
|
6
|
+
|
|
7
|
+
本软件包提供了一套专用的 Context 和 Hooks 系统,用于管理终端用户界面中的标准输入 (stdin)。它处理了解析原始 ANSI 转义序列的复杂性,支持特殊键、修饰键以及现代终端协议。
|
|
8
|
+
|
|
9
|
+
## 功能特性
|
|
10
|
+
|
|
11
|
+
- **易于集成**:即插即用的 Context 提供者 (`KeypressProvider`) 和 Hook (`useKeypress`)。
|
|
12
|
+
- **全面解析**:支持标准键、功能键 (F1-F12)、导航键等。
|
|
13
|
+
- **修饰键支持**:支持 Control、Alt/Meta 和 Shift 修饰键。
|
|
14
|
+
- **高级协议**:可选支持 Kitty 键盘协议。
|
|
15
|
+
- **鼠标与剪贴板**:内置鼠标事件过滤和 OSC 52 粘贴序列支持。
|
|
16
|
+
- **类型安全**:使用 TypeScript 编写,提供完整的类型定义。
|
|
17
|
+
|
|
18
|
+
## 安装
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install tinky-keypress
|
|
22
|
+
# 或
|
|
23
|
+
yarn add tinky-keypress
|
|
24
|
+
# 或
|
|
25
|
+
pnpm add tinky-keypress
|
|
26
|
+
# 或
|
|
27
|
+
bun add tinky-keypress
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 使用方法
|
|
31
|
+
|
|
32
|
+
使用 `KeypressProvider` 包裹应用根组件,并在任何子组件中使用 `useKeypress`。
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import React, { useState } from "react";
|
|
36
|
+
import { render, Text } from "tinky";
|
|
37
|
+
import { KeypressProvider, useKeypress } from "tinky-keypress";
|
|
38
|
+
|
|
39
|
+
function App() {
|
|
40
|
+
const [lastEvent, setLastEvent] = useState<string>("请按任意键...");
|
|
41
|
+
|
|
42
|
+
useKeypress(
|
|
43
|
+
(key) => {
|
|
44
|
+
const parts = [key.name];
|
|
45
|
+
if (key.ctrl) parts.push("Ctrl");
|
|
46
|
+
if (key.meta) parts.push("Alt");
|
|
47
|
+
if (key.shift) parts.push("Shift");
|
|
48
|
+
|
|
49
|
+
setLastEvent(
|
|
50
|
+
`检测到: ${parts.join("+")} (序列: ${JSON.stringify(key.sequence)})`,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (key.name === "q" && !key.ctrl) {
|
|
54
|
+
process.exit(0);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ isActive: true },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return <Text>{lastEvent}</Text>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const instance = render(
|
|
64
|
+
<KeypressProvider>
|
|
65
|
+
<App />
|
|
66
|
+
</KeypressProvider>,
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## API
|
|
71
|
+
|
|
72
|
+
### `<KeypressProvider />`
|
|
73
|
+
|
|
74
|
+
监听 `process.stdin` 的顶层 Provider。
|
|
75
|
+
|
|
76
|
+
| 属性 | 类型 | 默认值 | 描述 |
|
|
77
|
+
| --------------- | ----------- | ------- | ----------------------------------------- |
|
|
78
|
+
| `children` | `ReactNode` | N/A | 可以使用按键 Hooks 的子组件。 |
|
|
79
|
+
| `kittyProtocol` | `boolean` | `false` | 启用 Kitty 键盘协议支持(如果终端支持)。 |
|
|
80
|
+
|
|
81
|
+
### `useKeypress(handler, options)`
|
|
82
|
+
|
|
83
|
+
订阅按键事件的 Hook。
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
useKeypress(
|
|
87
|
+
handler: (key: Key) => void,
|
|
88
|
+
options: { isActive: boolean }
|
|
89
|
+
): void
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- **handler**: 当发生按键时调用的函数。
|
|
93
|
+
- **options.isActive**: 是否启用监听器(可用于焦点管理)。
|
|
94
|
+
|
|
95
|
+
### `Key` 接口
|
|
96
|
+
|
|
97
|
+
传递给处理程序的事件对象:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
interface Key {
|
|
101
|
+
name: string; // 按键名称 (例如 'a', 'enter', 'escape', 'up')
|
|
102
|
+
ctrl: boolean; // 是否按下了 Control 键
|
|
103
|
+
meta: boolean; // 是否按下了 Alt/Meta 键
|
|
104
|
+
shift: boolean; // 是否按下了 Shift 键
|
|
105
|
+
insertable: boolean; // 该按键是否产生可打印字符
|
|
106
|
+
sequence: string; // 接收到的原始 ANSI 序列
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 许可证
|
|
111
|
+
|
|
112
|
+
Apache-2.0
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Timeout for waiting for a return after a backslash to detect escaped newlines */
|
|
2
|
+
export declare const BACKSLASH_ENTER_TIMEOUT = 5;
|
|
3
|
+
/** Timeout for waiting for more characters in an escape sequence */
|
|
4
|
+
export declare const ESC_TIMEOUT = 50;
|
|
5
|
+
/** Timeout for waiting for paste end sequence */
|
|
6
|
+
export declare const PASTE_TIMEOUT = 30000;
|
|
7
|
+
/** Timeout for discriminating between a fast return key and a pasted newline */
|
|
8
|
+
export declare const FAST_RETURN_TIMEOUT = 30;
|
|
9
|
+
/** Focus event sequences */
|
|
10
|
+
export declare const FOCUS_IN = "\u001B[I";
|
|
11
|
+
export declare const FOCUS_OUT = "\u001B[O";
|
|
12
|
+
/**
|
|
13
|
+
* Represents a parsed key press event.
|
|
14
|
+
*/
|
|
15
|
+
export interface Key {
|
|
16
|
+
name: string;
|
|
17
|
+
ctrl: boolean;
|
|
18
|
+
meta: boolean;
|
|
19
|
+
shift: boolean;
|
|
20
|
+
insertable: boolean;
|
|
21
|
+
sequence: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Handler function type for keypress events.
|
|
25
|
+
*/
|
|
26
|
+
export type KeypressHandler = (key: Key) => void;
|
|
27
|
+
export interface KeypressContextValue {
|
|
28
|
+
subscribe: (handler: KeypressHandler) => void;
|
|
29
|
+
unsubscribe: (handler: KeypressHandler) => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Hook to access the Keypress context.
|
|
33
|
+
* Allows components to subscribe and unsubscribe to keypress events.
|
|
34
|
+
*
|
|
35
|
+
* @returns The Keypress context value containing subscribe and unsubscribe methods.
|
|
36
|
+
* @throws Error if used outside of a KeypressProvider.
|
|
37
|
+
*/
|
|
38
|
+
export declare function useKeypressContext(): KeypressContextValue;
|
|
39
|
+
/**
|
|
40
|
+
* Provider component for the Keypress context.
|
|
41
|
+
* Manages stdin data listening and parsing, and broadcasts key events to subscribers.
|
|
42
|
+
*/
|
|
43
|
+
export declare function KeypressProvider({ children, kittyProtocol, }: {
|
|
44
|
+
/** Child components */
|
|
45
|
+
children: React.ReactNode;
|
|
46
|
+
/** Whether Kitty keyboard protocol is enabled (from useTermcap) */
|
|
47
|
+
kittyProtocol?: boolean;
|
|
48
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
14
|
+
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);
|
|
15
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
16
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
17
|
+
function step(op) {
|
|
18
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
19
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
20
|
+
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;
|
|
21
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
22
|
+
switch (op[0]) {
|
|
23
|
+
case 0: case 1: t = op; break;
|
|
24
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
25
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
26
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
27
|
+
default:
|
|
28
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
29
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
30
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
31
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
32
|
+
if (t[2]) _.ops.pop();
|
|
33
|
+
_.trys.pop(); continue;
|
|
34
|
+
}
|
|
35
|
+
op = body.call(thisArg, _);
|
|
36
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
37
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var __values = (this && this.__values) || function(o) {
|
|
41
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
42
|
+
if (m) return m.call(o);
|
|
43
|
+
if (o && typeof o.length === "number") return {
|
|
44
|
+
next: function () {
|
|
45
|
+
if (o && i >= o.length) o = void 0;
|
|
46
|
+
return { value: o && o[i++], done: !o };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
50
|
+
};
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.FOCUS_OUT = exports.FOCUS_IN = exports.FAST_RETURN_TIMEOUT = exports.PASTE_TIMEOUT = exports.ESC_TIMEOUT = exports.BACKSLASH_ENTER_TIMEOUT = void 0;
|
|
53
|
+
exports.useKeypressContext = useKeypressContext;
|
|
54
|
+
exports.KeypressProvider = KeypressProvider;
|
|
55
|
+
var jsx_runtime_1 = require("react/jsx-runtime");
|
|
56
|
+
/* eslint-disable no-control-regex */
|
|
57
|
+
var react_1 = require("react");
|
|
58
|
+
var tinky_1 = require("tinky");
|
|
59
|
+
// ESC character constant (escape key)
|
|
60
|
+
var ESC = "\x1b";
|
|
61
|
+
// Simple check for mouse escape sequences to filter them out
|
|
62
|
+
// SGR format: ESC [ < ... m/M
|
|
63
|
+
// X11 format: ESC [ M ...
|
|
64
|
+
function isMouseSequence(sequence) {
|
|
65
|
+
if (!sequence.startsWith(ESC))
|
|
66
|
+
return false;
|
|
67
|
+
// SGR mouse: ESC [ < number ; number ; number m/M
|
|
68
|
+
if (/^\x1b\[<\d+;\d+;\d+[mM]$/.test(sequence))
|
|
69
|
+
return true;
|
|
70
|
+
// X11 mouse: ESC [ M followed by 3 chars
|
|
71
|
+
if (/^\x1b\[M[\s\S]{3}$/.test(sequence))
|
|
72
|
+
return true;
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
/** Timeout for waiting for a return after a backslash to detect escaped newlines */
|
|
76
|
+
exports.BACKSLASH_ENTER_TIMEOUT = 5;
|
|
77
|
+
/** Timeout for waiting for more characters in an escape sequence */
|
|
78
|
+
exports.ESC_TIMEOUT = 50;
|
|
79
|
+
/** Timeout for waiting for paste end sequence */
|
|
80
|
+
exports.PASTE_TIMEOUT = 30000;
|
|
81
|
+
/** Timeout for discriminating between a fast return key and a pasted newline */
|
|
82
|
+
exports.FAST_RETURN_TIMEOUT = 30;
|
|
83
|
+
/** Focus event sequences */
|
|
84
|
+
exports.FOCUS_IN = "\x1b[I";
|
|
85
|
+
exports.FOCUS_OUT = "\x1b[O";
|
|
86
|
+
// Parse the key itself
|
|
87
|
+
var KEY_INFO_MAP = {
|
|
88
|
+
"[200~": { name: "paste-start" },
|
|
89
|
+
"[201~": { name: "paste-end" },
|
|
90
|
+
"[[A": { name: "f1" },
|
|
91
|
+
"[[B": { name: "f2" },
|
|
92
|
+
"[[C": { name: "f3" },
|
|
93
|
+
"[[D": { name: "f4" },
|
|
94
|
+
"[[E": { name: "f5" },
|
|
95
|
+
"[1~": { name: "home" },
|
|
96
|
+
"[2~": { name: "insert" },
|
|
97
|
+
"[3~": { name: "delete" },
|
|
98
|
+
"[4~": { name: "end" },
|
|
99
|
+
"[5~": { name: "pageup" },
|
|
100
|
+
"[6~": { name: "pagedown" },
|
|
101
|
+
"[7~": { name: "home" },
|
|
102
|
+
"[8~": { name: "end" },
|
|
103
|
+
"[11~": { name: "f1" },
|
|
104
|
+
"[12~": { name: "f2" },
|
|
105
|
+
"[13~": { name: "f3" },
|
|
106
|
+
"[14~": { name: "f4" },
|
|
107
|
+
"[15~": { name: "f5" },
|
|
108
|
+
"[17~": { name: "f6" },
|
|
109
|
+
"[18~": { name: "f7" },
|
|
110
|
+
"[19~": { name: "f8" },
|
|
111
|
+
"[20~": { name: "f9" },
|
|
112
|
+
"[21~": { name: "f10" },
|
|
113
|
+
"[23~": { name: "f11" },
|
|
114
|
+
"[24~": { name: "f12" },
|
|
115
|
+
"[A": { name: "up" },
|
|
116
|
+
"[B": { name: "down" },
|
|
117
|
+
"[C": { name: "right" },
|
|
118
|
+
"[D": { name: "left" },
|
|
119
|
+
"[E": { name: "clear" },
|
|
120
|
+
"[F": { name: "end" },
|
|
121
|
+
"[H": { name: "home" },
|
|
122
|
+
"[P": { name: "f1" },
|
|
123
|
+
"[Q": { name: "f2" },
|
|
124
|
+
"[R": { name: "f3" },
|
|
125
|
+
"[S": { name: "f4" },
|
|
126
|
+
OA: { name: "up" },
|
|
127
|
+
OB: { name: "down" },
|
|
128
|
+
OC: { name: "right" },
|
|
129
|
+
OD: { name: "left" },
|
|
130
|
+
OE: { name: "clear" },
|
|
131
|
+
OF: { name: "end" },
|
|
132
|
+
OH: { name: "home" },
|
|
133
|
+
OP: { name: "f1" },
|
|
134
|
+
OQ: { name: "f2" },
|
|
135
|
+
OR: { name: "f3" },
|
|
136
|
+
OS: { name: "f4" },
|
|
137
|
+
"[[5~": { name: "pageup" },
|
|
138
|
+
"[[6~": { name: "pagedown" },
|
|
139
|
+
"[9u": { name: "tab" },
|
|
140
|
+
"[13u": { name: "return" },
|
|
141
|
+
"[27u": { name: "escape" },
|
|
142
|
+
"[32u": { name: "space" },
|
|
143
|
+
"[127u": { name: "backspace" },
|
|
144
|
+
"[57414u": { name: "return" }, // Numpad Enter
|
|
145
|
+
"[a": { name: "up", shift: true },
|
|
146
|
+
"[b": { name: "down", shift: true },
|
|
147
|
+
"[c": { name: "right", shift: true },
|
|
148
|
+
"[d": { name: "left", shift: true },
|
|
149
|
+
"[e": { name: "clear", shift: true },
|
|
150
|
+
"[2$": { name: "insert", shift: true },
|
|
151
|
+
"[3$": { name: "delete", shift: true },
|
|
152
|
+
"[5$": { name: "pageup", shift: true },
|
|
153
|
+
"[6$": { name: "pagedown", shift: true },
|
|
154
|
+
"[7$": { name: "home", shift: true },
|
|
155
|
+
"[8$": { name: "end", shift: true },
|
|
156
|
+
"[Z": { name: "tab", shift: true },
|
|
157
|
+
Oa: { name: "up", ctrl: true },
|
|
158
|
+
Ob: { name: "down", ctrl: true },
|
|
159
|
+
Oc: { name: "right", ctrl: true },
|
|
160
|
+
Od: { name: "left", ctrl: true },
|
|
161
|
+
Oe: { name: "clear", ctrl: true },
|
|
162
|
+
"[2^": { name: "insert", ctrl: true },
|
|
163
|
+
"[3^": { name: "delete", ctrl: true },
|
|
164
|
+
"[5^": { name: "pageup", ctrl: true },
|
|
165
|
+
"[6^": { name: "pagedown", ctrl: true },
|
|
166
|
+
"[7^": { name: "home", ctrl: true },
|
|
167
|
+
"[8^": { name: "end", ctrl: true },
|
|
168
|
+
};
|
|
169
|
+
var kUTF16SurrogateThreshold = 0x10000; // 2 ** 16
|
|
170
|
+
function charLengthAt(str, i) {
|
|
171
|
+
if (str.length <= i) {
|
|
172
|
+
// Pretend to move to the right. This is necessary to autocomplete while
|
|
173
|
+
// moving to the right.
|
|
174
|
+
return 1;
|
|
175
|
+
}
|
|
176
|
+
var code = str.codePointAt(i);
|
|
177
|
+
return code !== undefined && code >= kUTF16SurrogateThreshold ? 2 : 1;
|
|
178
|
+
}
|
|
179
|
+
var MAC_ALT_KEY_CHARACTER_MAP = {
|
|
180
|
+
"\u222B": "b", // "∫" back one word
|
|
181
|
+
"\u0192": "f", // "ƒ" forward one word
|
|
182
|
+
"\u00B5": "m", // "µ" toggle markup view
|
|
183
|
+
};
|
|
184
|
+
function nonKeyboardEventFilter(keypressHandler) {
|
|
185
|
+
return function (key) {
|
|
186
|
+
if (!isMouseSequence(key.sequence) &&
|
|
187
|
+
key.sequence !== exports.FOCUS_IN &&
|
|
188
|
+
key.sequence !== exports.FOCUS_OUT) {
|
|
189
|
+
keypressHandler(key);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Converts return keys pressed quickly after other keys into plain
|
|
195
|
+
* insertable return characters.
|
|
196
|
+
*
|
|
197
|
+
* This is to accommodate older terminals that paste text without bracketing.
|
|
198
|
+
*/
|
|
199
|
+
function bufferFastReturn(keypressHandler) {
|
|
200
|
+
var lastKeyTime = 0;
|
|
201
|
+
return function (key) {
|
|
202
|
+
var now = Date.now();
|
|
203
|
+
if (key.name === "return" && now - lastKeyTime <= exports.FAST_RETURN_TIMEOUT) {
|
|
204
|
+
keypressHandler(__assign(__assign({}, key), { name: "return", sequence: "\r", insertable: true }));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
keypressHandler(key);
|
|
208
|
+
}
|
|
209
|
+
lastKeyTime = now;
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Buffers "/" keys to see if they are followed return.
|
|
214
|
+
* Will flush the buffer if no data is received for DRAG_COMPLETION_TIMEOUT_MS
|
|
215
|
+
* or when a null key is received.
|
|
216
|
+
*/
|
|
217
|
+
function bufferBackslashEnter(keypressHandler) {
|
|
218
|
+
var bufferer = (function () {
|
|
219
|
+
var key, timeoutId, nextKey;
|
|
220
|
+
return __generator(this, function (_a) {
|
|
221
|
+
switch (_a.label) {
|
|
222
|
+
case 0:
|
|
223
|
+
if (!true) return [3 /*break*/, 3];
|
|
224
|
+
return [4 /*yield*/];
|
|
225
|
+
case 1:
|
|
226
|
+
key = _a.sent();
|
|
227
|
+
if (key == null) {
|
|
228
|
+
return [3 /*break*/, 0];
|
|
229
|
+
}
|
|
230
|
+
else if (key.sequence !== "\\") {
|
|
231
|
+
keypressHandler(key);
|
|
232
|
+
return [3 /*break*/, 0];
|
|
233
|
+
}
|
|
234
|
+
timeoutId = setTimeout(function () { return bufferer.next(null); }, exports.BACKSLASH_ENTER_TIMEOUT);
|
|
235
|
+
return [4 /*yield*/];
|
|
236
|
+
case 2:
|
|
237
|
+
nextKey = _a.sent();
|
|
238
|
+
clearTimeout(timeoutId);
|
|
239
|
+
if (nextKey === null) {
|
|
240
|
+
keypressHandler(key);
|
|
241
|
+
}
|
|
242
|
+
else if (nextKey.name === "return") {
|
|
243
|
+
keypressHandler(__assign(__assign({}, nextKey), { shift: true, sequence: "\r" }));
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
keypressHandler(key);
|
|
247
|
+
keypressHandler(nextKey);
|
|
248
|
+
}
|
|
249
|
+
return [3 /*break*/, 0];
|
|
250
|
+
case 3: return [2 /*return*/];
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
})();
|
|
254
|
+
bufferer.next(); // prime the generator so it starts listening.
|
|
255
|
+
return function (key) { return bufferer.next(key); };
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Buffers paste events between paste-start and paste-end sequences.
|
|
259
|
+
* Will flush the buffer if no data is received for PASTE_TIMEOUT ms or
|
|
260
|
+
* when a null key is received.
|
|
261
|
+
*/
|
|
262
|
+
function bufferPaste(keypressHandler) {
|
|
263
|
+
var bufferer = (function () {
|
|
264
|
+
var key, buffer, timeoutId;
|
|
265
|
+
return __generator(this, function (_a) {
|
|
266
|
+
switch (_a.label) {
|
|
267
|
+
case 0:
|
|
268
|
+
if (!true) return [3 /*break*/, 5];
|
|
269
|
+
return [4 /*yield*/];
|
|
270
|
+
case 1:
|
|
271
|
+
key = _a.sent();
|
|
272
|
+
if (key === null) {
|
|
273
|
+
return [3 /*break*/, 0];
|
|
274
|
+
}
|
|
275
|
+
else if (key.name !== "paste-start") {
|
|
276
|
+
keypressHandler(key);
|
|
277
|
+
return [3 /*break*/, 0];
|
|
278
|
+
}
|
|
279
|
+
buffer = "";
|
|
280
|
+
_a.label = 2;
|
|
281
|
+
case 2:
|
|
282
|
+
if (!true) return [3 /*break*/, 4];
|
|
283
|
+
timeoutId = setTimeout(function () { return bufferer.next(null); }, exports.PASTE_TIMEOUT);
|
|
284
|
+
return [4 /*yield*/];
|
|
285
|
+
case 3:
|
|
286
|
+
key = _a.sent();
|
|
287
|
+
clearTimeout(timeoutId);
|
|
288
|
+
if (key === null) {
|
|
289
|
+
// Paste timeout occurred - data may be truncated
|
|
290
|
+
return [3 /*break*/, 4];
|
|
291
|
+
}
|
|
292
|
+
if (key.name === "paste-end") {
|
|
293
|
+
return [3 /*break*/, 4];
|
|
294
|
+
}
|
|
295
|
+
buffer += key.sequence;
|
|
296
|
+
return [3 /*break*/, 2];
|
|
297
|
+
case 4:
|
|
298
|
+
if (buffer.length > 0) {
|
|
299
|
+
keypressHandler({
|
|
300
|
+
name: "paste",
|
|
301
|
+
ctrl: false,
|
|
302
|
+
meta: false,
|
|
303
|
+
shift: false,
|
|
304
|
+
insertable: true,
|
|
305
|
+
sequence: buffer,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return [3 /*break*/, 0];
|
|
309
|
+
case 5: return [2 /*return*/];
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
})();
|
|
313
|
+
bufferer.next(); // prime the generator so it starts listening.
|
|
314
|
+
return function (key) { return bufferer.next(key); };
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Turns raw data strings into keypress events sent to the provided handler.
|
|
318
|
+
* Buffers escape sequences until a full sequence is received or
|
|
319
|
+
* until a timeout occurs.
|
|
320
|
+
*/
|
|
321
|
+
function createDataListener(keypressHandler) {
|
|
322
|
+
var parser = emitKeys(keypressHandler);
|
|
323
|
+
parser.next(); // prime the generator so it starts listening.
|
|
324
|
+
var timeoutId;
|
|
325
|
+
return function (data) {
|
|
326
|
+
var e_1, _a;
|
|
327
|
+
clearTimeout(timeoutId);
|
|
328
|
+
try {
|
|
329
|
+
for (var data_1 = __values(data), data_1_1 = data_1.next(); !data_1_1.done; data_1_1 = data_1.next()) {
|
|
330
|
+
var char = data_1_1.value;
|
|
331
|
+
parser.next(char);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
335
|
+
finally {
|
|
336
|
+
try {
|
|
337
|
+
if (data_1_1 && !data_1_1.done && (_a = data_1.return)) _a.call(data_1);
|
|
338
|
+
}
|
|
339
|
+
finally { if (e_1) throw e_1.error; }
|
|
340
|
+
}
|
|
341
|
+
if (data.length !== 0) {
|
|
342
|
+
timeoutId = setTimeout(function () { return parser.next(""); }, exports.ESC_TIMEOUT);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Translates raw keypress characters into key events.
|
|
348
|
+
* Buffers escape sequences until a full sequence is received or
|
|
349
|
+
* until an empty string is sent to indicate a timeout.
|
|
350
|
+
*/
|
|
351
|
+
function emitKeys(keypressHandler) {
|
|
352
|
+
var ch, sequence, escaped, name, ctrl, meta, shift, code, insertable, modifier, buffer, next, afterEsc, match, base64Data, decoded, cmdStart, cmd, match, keyInfo, codeNumber;
|
|
353
|
+
var _a, _b, _c, _d;
|
|
354
|
+
return __generator(this, function (_e) {
|
|
355
|
+
switch (_e.label) {
|
|
356
|
+
case 0:
|
|
357
|
+
if (!true) return [3 /*break*/, 41];
|
|
358
|
+
return [4 /*yield*/];
|
|
359
|
+
case 1:
|
|
360
|
+
ch = _e.sent();
|
|
361
|
+
sequence = ch;
|
|
362
|
+
escaped = false;
|
|
363
|
+
name = undefined;
|
|
364
|
+
ctrl = false;
|
|
365
|
+
meta = false;
|
|
366
|
+
shift = false;
|
|
367
|
+
code = undefined;
|
|
368
|
+
insertable = false;
|
|
369
|
+
if (!(ch === ESC)) return [3 /*break*/, 4];
|
|
370
|
+
escaped = true;
|
|
371
|
+
return [4 /*yield*/];
|
|
372
|
+
case 2:
|
|
373
|
+
ch = _e.sent();
|
|
374
|
+
sequence += ch;
|
|
375
|
+
if (!(ch === ESC)) return [3 /*break*/, 4];
|
|
376
|
+
return [4 /*yield*/];
|
|
377
|
+
case 3:
|
|
378
|
+
ch = _e.sent();
|
|
379
|
+
sequence += ch;
|
|
380
|
+
_e.label = 4;
|
|
381
|
+
case 4:
|
|
382
|
+
if (!(escaped && (ch === "O" || ch === "[" || ch === "]"))) return [3 /*break*/, 39];
|
|
383
|
+
// ANSI escape sequence
|
|
384
|
+
code = ch;
|
|
385
|
+
modifier = 0;
|
|
386
|
+
if (!(ch === "]")) return [3 /*break*/, 11];
|
|
387
|
+
buffer = "";
|
|
388
|
+
_e.label = 5;
|
|
389
|
+
case 5:
|
|
390
|
+
if (!true) return [3 /*break*/, 10];
|
|
391
|
+
return [4 /*yield*/];
|
|
392
|
+
case 6:
|
|
393
|
+
next = _e.sent();
|
|
394
|
+
if (!(next === "" || next === "\u0007")) return [3 /*break*/, 7];
|
|
395
|
+
return [3 /*break*/, 10];
|
|
396
|
+
case 7:
|
|
397
|
+
if (!(next === ESC)) return [3 /*break*/, 9];
|
|
398
|
+
return [4 /*yield*/];
|
|
399
|
+
case 8:
|
|
400
|
+
afterEsc = _e.sent();
|
|
401
|
+
if (afterEsc === "" || afterEsc === "\\") {
|
|
402
|
+
return [3 /*break*/, 10];
|
|
403
|
+
}
|
|
404
|
+
buffer += next + afterEsc;
|
|
405
|
+
return [3 /*break*/, 5];
|
|
406
|
+
case 9:
|
|
407
|
+
buffer += next;
|
|
408
|
+
return [3 /*break*/, 5];
|
|
409
|
+
case 10:
|
|
410
|
+
match = /^52;[cp];(.*)$/.exec(buffer);
|
|
411
|
+
if (match) {
|
|
412
|
+
try {
|
|
413
|
+
base64Data = match[1];
|
|
414
|
+
decoded = Buffer.from(base64Data, "base64").toString("utf-8");
|
|
415
|
+
keypressHandler({
|
|
416
|
+
name: "paste",
|
|
417
|
+
ctrl: false,
|
|
418
|
+
meta: false,
|
|
419
|
+
shift: false,
|
|
420
|
+
insertable: true,
|
|
421
|
+
sequence: decoded,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
catch (_f) {
|
|
425
|
+
// Ignore error
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return [3 /*break*/, 0]; // resume main loop
|
|
429
|
+
case 11:
|
|
430
|
+
if (!(ch === "O")) return [3 /*break*/, 15];
|
|
431
|
+
return [4 /*yield*/];
|
|
432
|
+
case 12:
|
|
433
|
+
// ESC O letter
|
|
434
|
+
// ESC O modifier letter
|
|
435
|
+
ch = _e.sent();
|
|
436
|
+
sequence += ch;
|
|
437
|
+
if (!(ch >= "0" && ch <= "9")) return [3 /*break*/, 14];
|
|
438
|
+
modifier = parseInt(ch, 10) - 1;
|
|
439
|
+
return [4 /*yield*/];
|
|
440
|
+
case 13:
|
|
441
|
+
ch = _e.sent();
|
|
442
|
+
sequence += ch;
|
|
443
|
+
_e.label = 14;
|
|
444
|
+
case 14:
|
|
445
|
+
code += ch;
|
|
446
|
+
return [3 /*break*/, 38];
|
|
447
|
+
case 15:
|
|
448
|
+
if (!(ch === "[")) return [3 /*break*/, 38];
|
|
449
|
+
return [4 /*yield*/];
|
|
450
|
+
case 16:
|
|
451
|
+
// ESC [ letter
|
|
452
|
+
// ESC [ modifier letter
|
|
453
|
+
// ESC [ [ modifier letter
|
|
454
|
+
// ESC [ [ num char
|
|
455
|
+
ch = _e.sent();
|
|
456
|
+
sequence += ch;
|
|
457
|
+
if (!(ch === "[")) return [3 /*break*/, 18];
|
|
458
|
+
// \x1b[[A
|
|
459
|
+
// ^--- escape codes might have a second bracket
|
|
460
|
+
code += ch;
|
|
461
|
+
return [4 /*yield*/];
|
|
462
|
+
case 17:
|
|
463
|
+
ch = _e.sent();
|
|
464
|
+
sequence += ch;
|
|
465
|
+
_e.label = 18;
|
|
466
|
+
case 18:
|
|
467
|
+
cmdStart = sequence.length - 1;
|
|
468
|
+
_e.label = 19;
|
|
469
|
+
case 19:
|
|
470
|
+
if (!(ch >= "0" && ch <= "9")) return [3 /*break*/, 21];
|
|
471
|
+
return [4 /*yield*/];
|
|
472
|
+
case 20:
|
|
473
|
+
ch = _e.sent();
|
|
474
|
+
sequence += ch;
|
|
475
|
+
return [3 /*break*/, 19];
|
|
476
|
+
case 21:
|
|
477
|
+
if (!(ch === ";")) return [3 /*break*/, 28];
|
|
478
|
+
_e.label = 22;
|
|
479
|
+
case 22:
|
|
480
|
+
if (!(ch === ";")) return [3 /*break*/, 27];
|
|
481
|
+
return [4 /*yield*/];
|
|
482
|
+
case 23:
|
|
483
|
+
ch = _e.sent();
|
|
484
|
+
sequence += ch;
|
|
485
|
+
_e.label = 24;
|
|
486
|
+
case 24:
|
|
487
|
+
if (!(ch >= "0" && ch <= "9")) return [3 /*break*/, 26];
|
|
488
|
+
return [4 /*yield*/];
|
|
489
|
+
case 25:
|
|
490
|
+
ch = _e.sent();
|
|
491
|
+
sequence += ch;
|
|
492
|
+
return [3 /*break*/, 24];
|
|
493
|
+
case 26: return [3 /*break*/, 22];
|
|
494
|
+
case 27: return [3 /*break*/, 37];
|
|
495
|
+
case 28:
|
|
496
|
+
if (!(ch === "<")) return [3 /*break*/, 33];
|
|
497
|
+
return [4 /*yield*/];
|
|
498
|
+
case 29:
|
|
499
|
+
// SGR mouse mode
|
|
500
|
+
ch = _e.sent();
|
|
501
|
+
sequence += ch;
|
|
502
|
+
_e.label = 30;
|
|
503
|
+
case 30:
|
|
504
|
+
if (!(ch === "" || ch === ";" || (ch >= "0" && ch <= "9"))) return [3 /*break*/, 32];
|
|
505
|
+
return [4 /*yield*/];
|
|
506
|
+
case 31:
|
|
507
|
+
ch = _e.sent();
|
|
508
|
+
sequence += ch;
|
|
509
|
+
return [3 /*break*/, 30];
|
|
510
|
+
case 32: return [3 /*break*/, 37];
|
|
511
|
+
case 33:
|
|
512
|
+
if (!(ch === "M")) return [3 /*break*/, 37];
|
|
513
|
+
return [4 /*yield*/];
|
|
514
|
+
case 34:
|
|
515
|
+
// X11 mouse mode
|
|
516
|
+
// three characters after 'M'
|
|
517
|
+
ch = _e.sent();
|
|
518
|
+
sequence += ch;
|
|
519
|
+
return [4 /*yield*/];
|
|
520
|
+
case 35:
|
|
521
|
+
ch = _e.sent();
|
|
522
|
+
sequence += ch;
|
|
523
|
+
return [4 /*yield*/];
|
|
524
|
+
case 36:
|
|
525
|
+
ch = _e.sent();
|
|
526
|
+
sequence += ch;
|
|
527
|
+
_e.label = 37;
|
|
528
|
+
case 37:
|
|
529
|
+
cmd = sequence.slice(cmdStart);
|
|
530
|
+
match = void 0;
|
|
531
|
+
if ((match = /^(\d+)(?:;(\d+))?(?:;(\d+))?([~^$u])$/.exec(cmd))) {
|
|
532
|
+
if (match[1] === "27" && match[3] && match[4] === "~") {
|
|
533
|
+
// modifyOtherKeys format: CSI 27 ; modifier ; key ~
|
|
534
|
+
// Treat as CSI u: key + 'u'
|
|
535
|
+
code += match[3] + "u";
|
|
536
|
+
modifier = parseInt((_a = match[2]) !== null && _a !== void 0 ? _a : "1", 10) - 1;
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
code += match[1] + match[4];
|
|
540
|
+
// Defaults to '1' if no modifier exists, resulting in a 0 modifier value
|
|
541
|
+
modifier = parseInt((_b = match[2]) !== null && _b !== void 0 ? _b : "1", 10) - 1;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
else if ((match = /^(\d+)?(?:;(\d+))?([A-Za-z])$/.exec(cmd))) {
|
|
545
|
+
code += match[3];
|
|
546
|
+
modifier = parseInt((_d = (_c = match[2]) !== null && _c !== void 0 ? _c : match[1]) !== null && _d !== void 0 ? _d : "1", 10) - 1;
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
code += cmd;
|
|
550
|
+
}
|
|
551
|
+
_e.label = 38;
|
|
552
|
+
case 38:
|
|
553
|
+
// Parse the key modifier
|
|
554
|
+
ctrl = !!(modifier & 4);
|
|
555
|
+
meta = !!(modifier & 10); // use 10 to catch both alt (2) and meta (8).
|
|
556
|
+
shift = !!(modifier & 1);
|
|
557
|
+
keyInfo = KEY_INFO_MAP[code];
|
|
558
|
+
if (keyInfo) {
|
|
559
|
+
name = keyInfo.name;
|
|
560
|
+
if (keyInfo.shift) {
|
|
561
|
+
shift = true;
|
|
562
|
+
}
|
|
563
|
+
if (keyInfo.ctrl) {
|
|
564
|
+
ctrl = true;
|
|
565
|
+
}
|
|
566
|
+
if (name === "space" && !ctrl && !meta) {
|
|
567
|
+
sequence = " ";
|
|
568
|
+
insertable = true;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
name = "undefined";
|
|
573
|
+
if ((ctrl || meta) && (code.endsWith("u") || code.endsWith("~"))) {
|
|
574
|
+
codeNumber = parseInt(code.slice(1, -1), 10);
|
|
575
|
+
if (codeNumber >= "a".charCodeAt(0) &&
|
|
576
|
+
codeNumber <= "z".charCodeAt(0)) {
|
|
577
|
+
name = String.fromCharCode(codeNumber);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return [3 /*break*/, 40];
|
|
582
|
+
case 39:
|
|
583
|
+
if (ch === "\r") {
|
|
584
|
+
// carriage return
|
|
585
|
+
name = "return";
|
|
586
|
+
meta = escaped;
|
|
587
|
+
}
|
|
588
|
+
else if (ch === "\n") {
|
|
589
|
+
// Enter, should have been called linefeed
|
|
590
|
+
name = "enter";
|
|
591
|
+
meta = escaped;
|
|
592
|
+
}
|
|
593
|
+
else if (ch === "\t") {
|
|
594
|
+
// tab
|
|
595
|
+
name = "tab";
|
|
596
|
+
meta = escaped;
|
|
597
|
+
}
|
|
598
|
+
else if (ch === "\b" || ch === "\x7f") {
|
|
599
|
+
// backspace or ctrl+h
|
|
600
|
+
name = "backspace";
|
|
601
|
+
meta = escaped;
|
|
602
|
+
}
|
|
603
|
+
else if (ch === ESC) {
|
|
604
|
+
// escape key
|
|
605
|
+
name = "escape";
|
|
606
|
+
meta = escaped;
|
|
607
|
+
}
|
|
608
|
+
else if (ch === " ") {
|
|
609
|
+
name = "space";
|
|
610
|
+
meta = escaped;
|
|
611
|
+
insertable = true;
|
|
612
|
+
}
|
|
613
|
+
else if (!escaped && ch <= "\x1a") {
|
|
614
|
+
// ctrl+letter
|
|
615
|
+
name = String.fromCharCode(ch.charCodeAt(0) + "a".charCodeAt(0) - 1);
|
|
616
|
+
ctrl = true;
|
|
617
|
+
}
|
|
618
|
+
else if (/^[0-9A-Za-z]$/.exec(ch) !== null) {
|
|
619
|
+
// Letter, number, shift+letter
|
|
620
|
+
name = ch.toLowerCase();
|
|
621
|
+
shift = /^[A-Z]$/.exec(ch) !== null;
|
|
622
|
+
meta = escaped;
|
|
623
|
+
insertable = true;
|
|
624
|
+
}
|
|
625
|
+
else if (MAC_ALT_KEY_CHARACTER_MAP[ch] && process.platform === "darwin") {
|
|
626
|
+
name = MAC_ALT_KEY_CHARACTER_MAP[ch];
|
|
627
|
+
meta = true;
|
|
628
|
+
}
|
|
629
|
+
else if (sequence === "".concat(ESC).concat(ESC)) {
|
|
630
|
+
// Double escape
|
|
631
|
+
name = "escape";
|
|
632
|
+
meta = true;
|
|
633
|
+
// Emit first escape key here, then continue processing
|
|
634
|
+
keypressHandler({
|
|
635
|
+
name: "escape",
|
|
636
|
+
ctrl: ctrl,
|
|
637
|
+
meta: meta,
|
|
638
|
+
shift: shift,
|
|
639
|
+
insertable: false,
|
|
640
|
+
sequence: ESC,
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
else if (escaped) {
|
|
644
|
+
// Escape sequence timeout
|
|
645
|
+
name = ch.length ? undefined : "escape";
|
|
646
|
+
meta = true;
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
// Any other character is considered printable.
|
|
650
|
+
insertable = true;
|
|
651
|
+
}
|
|
652
|
+
_e.label = 40;
|
|
653
|
+
case 40:
|
|
654
|
+
if ((sequence.length !== 0 && (name !== undefined || escaped)) ||
|
|
655
|
+
charLengthAt(sequence, 0) === sequence.length) {
|
|
656
|
+
keypressHandler({
|
|
657
|
+
name: name || "",
|
|
658
|
+
ctrl: ctrl,
|
|
659
|
+
meta: meta,
|
|
660
|
+
shift: shift,
|
|
661
|
+
insertable: insertable,
|
|
662
|
+
sequence: sequence,
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
return [3 /*break*/, 0];
|
|
666
|
+
case 41: return [2 /*return*/];
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
var KeypressContext = (0, react_1.createContext)(undefined);
|
|
671
|
+
/**
|
|
672
|
+
* Hook to access the Keypress context.
|
|
673
|
+
* Allows components to subscribe and unsubscribe to keypress events.
|
|
674
|
+
*
|
|
675
|
+
* @returns The Keypress context value containing subscribe and unsubscribe methods.
|
|
676
|
+
* @throws Error if used outside of a KeypressProvider.
|
|
677
|
+
*/
|
|
678
|
+
function useKeypressContext() {
|
|
679
|
+
var context = (0, react_1.useContext)(KeypressContext);
|
|
680
|
+
if (!context) {
|
|
681
|
+
throw new Error("useKeypressContext must be used within a KeypressProvider");
|
|
682
|
+
}
|
|
683
|
+
return context;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Provider component for the Keypress context.
|
|
687
|
+
* Manages stdin data listening and parsing, and broadcasts key events to subscribers.
|
|
688
|
+
*/
|
|
689
|
+
function KeypressProvider(_a) {
|
|
690
|
+
var children = _a.children, _b = _a.kittyProtocol, kittyProtocol = _b === void 0 ? false : _b;
|
|
691
|
+
var stdin = (0, tinky_1.useStdin)().stdin;
|
|
692
|
+
var subscribers = (0, react_1.useRef)(new Set()).current;
|
|
693
|
+
var subscribe = (0, react_1.useCallback)(function (handler) { return subscribers.add(handler); }, [subscribers]);
|
|
694
|
+
var unsubscribe = (0, react_1.useCallback)(function (handler) { return subscribers.delete(handler); }, [subscribers]);
|
|
695
|
+
var broadcast = (0, react_1.useCallback)(function (key) { return subscribers.forEach(function (handler) { return handler(key); }); }, [subscribers]);
|
|
696
|
+
(0, react_1.useEffect)(function () {
|
|
697
|
+
process.stdin.setEncoding("utf8"); // Make data events emit strings
|
|
698
|
+
var processor = nonKeyboardEventFilter(broadcast);
|
|
699
|
+
if (!kittyProtocol) {
|
|
700
|
+
processor = bufferFastReturn(processor);
|
|
701
|
+
}
|
|
702
|
+
processor = bufferBackslashEnter(processor);
|
|
703
|
+
processor = bufferPaste(processor);
|
|
704
|
+
var dataListener = createDataListener(processor);
|
|
705
|
+
if (!stdin)
|
|
706
|
+
return;
|
|
707
|
+
var stdinStream = stdin;
|
|
708
|
+
stdinStream.on("data", dataListener);
|
|
709
|
+
return function () {
|
|
710
|
+
stdinStream.removeListener("data", dataListener);
|
|
711
|
+
};
|
|
712
|
+
}, [stdin, broadcast]);
|
|
713
|
+
return ((0, jsx_runtime_1.jsx)(KeypressContext.Provider, { value: { subscribe: subscribe, unsubscribe: unsubscribe }, children: children }));
|
|
714
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { KeypressHandler, Key } from "../contexts/KeypressContext.js";
|
|
2
|
+
export type { Key };
|
|
3
|
+
/**
|
|
4
|
+
* A hook that listens for keypress events from stdin.
|
|
5
|
+
*
|
|
6
|
+
* @param onKeypress - The callback function to execute on each keypress.
|
|
7
|
+
* @param options - Options to control the hook's behavior.
|
|
8
|
+
* @param options.isActive - Whether the hook should be actively listening for input.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useKeypress(onKeypress: KeypressHandler, { isActive }: {
|
|
11
|
+
isActive: boolean;
|
|
12
|
+
}): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useKeypress = useKeypress;
|
|
4
|
+
var react_1 = require("react");
|
|
5
|
+
var KeypressContext_js_1 = require("../contexts/KeypressContext.js");
|
|
6
|
+
/**
|
|
7
|
+
* A hook that listens for keypress events from stdin.
|
|
8
|
+
*
|
|
9
|
+
* @param onKeypress - The callback function to execute on each keypress.
|
|
10
|
+
* @param options - Options to control the hook's behavior.
|
|
11
|
+
* @param options.isActive - Whether the hook should be actively listening for input.
|
|
12
|
+
*/
|
|
13
|
+
function useKeypress(onKeypress, _a) {
|
|
14
|
+
var isActive = _a.isActive;
|
|
15
|
+
var _b = (0, KeypressContext_js_1.useKeypressContext)(), subscribe = _b.subscribe, unsubscribe = _b.unsubscribe;
|
|
16
|
+
(0, react_1.useEffect)(function () {
|
|
17
|
+
if (!isActive) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
subscribe(onKeypress);
|
|
21
|
+
return function () {
|
|
22
|
+
unsubscribe(onKeypress);
|
|
23
|
+
};
|
|
24
|
+
}, [isActive, onKeypress, subscribe, unsubscribe]);
|
|
25
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keypress Context and Provider
|
|
3
|
+
*/
|
|
4
|
+
export { KeypressProvider, useKeypressContext, BACKSLASH_ENTER_TIMEOUT, ESC_TIMEOUT, PASTE_TIMEOUT, FAST_RETURN_TIMEOUT, FOCUS_IN, FOCUS_OUT, type Key, type KeypressHandler, type KeypressContextValue, } from "./contexts/KeypressContext.js";
|
|
5
|
+
/**
|
|
6
|
+
* Keypress hook
|
|
7
|
+
*/
|
|
8
|
+
export { useKeypress } from "./hooks/use-keypress.js";
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useKeypress = exports.FOCUS_OUT = exports.FOCUS_IN = exports.FAST_RETURN_TIMEOUT = exports.PASTE_TIMEOUT = exports.ESC_TIMEOUT = exports.BACKSLASH_ENTER_TIMEOUT = exports.useKeypressContext = exports.KeypressProvider = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Keypress Context and Provider
|
|
6
|
+
*/
|
|
7
|
+
var KeypressContext_js_1 = require("./contexts/KeypressContext.js");
|
|
8
|
+
Object.defineProperty(exports, "KeypressProvider", { enumerable: true, get: function () { return KeypressContext_js_1.KeypressProvider; } });
|
|
9
|
+
Object.defineProperty(exports, "useKeypressContext", { enumerable: true, get: function () { return KeypressContext_js_1.useKeypressContext; } });
|
|
10
|
+
Object.defineProperty(exports, "BACKSLASH_ENTER_TIMEOUT", { enumerable: true, get: function () { return KeypressContext_js_1.BACKSLASH_ENTER_TIMEOUT; } });
|
|
11
|
+
Object.defineProperty(exports, "ESC_TIMEOUT", { enumerable: true, get: function () { return KeypressContext_js_1.ESC_TIMEOUT; } });
|
|
12
|
+
Object.defineProperty(exports, "PASTE_TIMEOUT", { enumerable: true, get: function () { return KeypressContext_js_1.PASTE_TIMEOUT; } });
|
|
13
|
+
Object.defineProperty(exports, "FAST_RETURN_TIMEOUT", { enumerable: true, get: function () { return KeypressContext_js_1.FAST_RETURN_TIMEOUT; } });
|
|
14
|
+
Object.defineProperty(exports, "FOCUS_IN", { enumerable: true, get: function () { return KeypressContext_js_1.FOCUS_IN; } });
|
|
15
|
+
Object.defineProperty(exports, "FOCUS_OUT", { enumerable: true, get: function () { return KeypressContext_js_1.FOCUS_OUT; } });
|
|
16
|
+
/**
|
|
17
|
+
* Keypress hook
|
|
18
|
+
*/
|
|
19
|
+
var use_keypress_js_1 = require("./hooks/use-keypress.js");
|
|
20
|
+
Object.defineProperty(exports, "useKeypress", { enumerable: true, get: function () { return use_keypress_js_1.useKeypress; } });
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tinky-keypress",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Keypress handling context and hooks for Tinky",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"tinky",
|
|
7
|
+
"react",
|
|
8
|
+
"cli",
|
|
9
|
+
"terminal",
|
|
10
|
+
"keypress",
|
|
11
|
+
"input",
|
|
12
|
+
"keyboard",
|
|
13
|
+
"stdin",
|
|
14
|
+
"tui"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/ByteLandTechnology/tinky-keypress#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/ByteLandTechnology/tinky-keypress/issues"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/ByteLandTechnology/tinky-keypress.git"
|
|
23
|
+
},
|
|
24
|
+
"license": "Apache-2.0",
|
|
25
|
+
"author": {
|
|
26
|
+
"name": "ByteLandTechnology"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"main": "./lib/index.js",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"test": "bun test",
|
|
32
|
+
"build": "tsc && npm run docs",
|
|
33
|
+
"lint": "eslint src tests",
|
|
34
|
+
"prepublish": "npm run build",
|
|
35
|
+
"prepare": "husky",
|
|
36
|
+
"docs": "typedoc --plugin typedoc-plugin-markdown --disableSources --out docs/api && prettier --write docs/api"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"tinky": "^1.1.2"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"./bin/*",
|
|
43
|
+
"./lib/*"
|
|
44
|
+
],
|
|
45
|
+
"typings": "./lib/index.d.ts",
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@commitlint/cli": "^20.3.0",
|
|
48
|
+
"@commitlint/config-conventional": "^20.3.0",
|
|
49
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
50
|
+
"@semantic-release/git": "^10.0.1",
|
|
51
|
+
"@semantic-release/github": "^12.0.0",
|
|
52
|
+
"@semantic-release/npm": "^13.1.3",
|
|
53
|
+
"@types/bun": "^1.3.6",
|
|
54
|
+
"eslint": "^9.39.2",
|
|
55
|
+
"eslint-plugin-react": "^7.37.5",
|
|
56
|
+
"husky": "^9.1.7",
|
|
57
|
+
"jiti": "^2.6.1",
|
|
58
|
+
"lint-staged": "^16.2.7",
|
|
59
|
+
"prettier": "^3.7.4",
|
|
60
|
+
"typedoc": "^0.28.16",
|
|
61
|
+
"typedoc-plugin-markdown": "^4.9.0",
|
|
62
|
+
"typescript-eslint": "^8.53.0"
|
|
63
|
+
},
|
|
64
|
+
"commitlint": {
|
|
65
|
+
"extends": [
|
|
66
|
+
"@commitlint/config-conventional"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"lint-staged": {
|
|
70
|
+
"*.{js,ts,jsx,tsx,json,md,yaml,yml}": "prettier --write"
|
|
71
|
+
}
|
|
72
|
+
}
|