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.
@@ -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
@@ -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
+ }