ink 6.5.1 → 6.7.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/build/components/App.d.ts +7 -48
- package/build/components/App.js +259 -225
- package/build/components/App.js.map +1 -1
- package/build/components/CursorContext.d.ts +11 -0
- package/build/components/CursorContext.js +8 -0
- package/build/components/CursorContext.js.map +1 -0
- package/build/components/ErrorBoundary.d.ts +18 -0
- package/build/components/ErrorBoundary.js +23 -0
- package/build/components/ErrorBoundary.js.map +1 -0
- package/build/cursor-helpers.d.ts +38 -0
- package/build/cursor-helpers.js +56 -0
- package/build/cursor-helpers.js.map +1 -0
- package/build/hooks/use-cursor.d.ts +12 -0
- package/build/hooks/use-cursor.js +29 -0
- package/build/hooks/use-cursor.js.map +1 -0
- package/build/hooks/use-focus-manager.d.ts +1 -1
- package/build/hooks/use-focus-manager.js +1 -1
- package/build/hooks/use-input.d.ts +38 -0
- package/build/hooks/use-input.js +33 -2
- package/build/hooks/use-input.js.map +1 -1
- package/build/index.d.ts +4 -0
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/ink.d.ts +32 -0
- package/build/ink.js +258 -31
- package/build/ink.js.map +1 -1
- package/build/kitty-keyboard.d.ts +23 -0
- package/build/kitty-keyboard.js +32 -0
- package/build/kitty-keyboard.js.map +1 -0
- package/build/log-update.d.ts +6 -1
- package/build/log-update.js +166 -40
- package/build/log-update.js.map +1 -1
- package/build/parse-keypress.d.ts +8 -0
- package/build/parse-keypress.js +270 -2
- package/build/parse-keypress.js.map +1 -1
- package/build/reconciler.js +11 -1
- package/build/reconciler.js.map +1 -1
- package/build/render.d.ts +23 -1
- package/build/render.js +7 -2
- package/build/render.js.map +1 -1
- package/build/styles.d.ts +8 -8
- package/build/write-synchronized.d.ts +4 -0
- package/build/write-synchronized.js +7 -0
- package/build/write-synchronized.js.map +1 -0
- package/package.json +18 -15
- package/readme.md +180 -12
package/build/log-update.js
CHANGED
|
@@ -1,108 +1,234 @@
|
|
|
1
1
|
import ansiEscapes from 'ansi-escapes';
|
|
2
2
|
import cliCursor from 'cli-cursor';
|
|
3
|
+
import { cursorPositionChanged, buildCursorSuffix, buildCursorOnlySequence, buildReturnToBottomPrefix, hideCursorEscape, } from './cursor-helpers.js';
|
|
4
|
+
// Count visible lines in a string, ignoring the trailing empty element
|
|
5
|
+
// that `split('\n')` produces when the string ends with '\n'.
|
|
6
|
+
const visibleLineCount = (lines, str) => str.endsWith('\n') ? lines.length - 1 : lines.length;
|
|
3
7
|
const createStandard = (stream, { showCursor = false } = {}) => {
|
|
4
8
|
let previousLineCount = 0;
|
|
5
9
|
let previousOutput = '';
|
|
6
10
|
let hasHiddenCursor = false;
|
|
11
|
+
let cursorPosition;
|
|
12
|
+
let cursorDirty = false;
|
|
13
|
+
let previousCursorPosition;
|
|
14
|
+
let cursorWasShown = false;
|
|
15
|
+
const getActiveCursor = () => (cursorDirty ? cursorPosition : undefined);
|
|
16
|
+
const hasChanges = (str, activeCursor) => {
|
|
17
|
+
const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
|
|
18
|
+
return str !== previousOutput || cursorChanged;
|
|
19
|
+
};
|
|
7
20
|
const render = (str) => {
|
|
8
21
|
if (!showCursor && !hasHiddenCursor) {
|
|
9
|
-
cliCursor.hide();
|
|
22
|
+
cliCursor.hide(stream);
|
|
10
23
|
hasHiddenCursor = true;
|
|
11
24
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
25
|
+
// Only use cursor if setCursorPosition was called since last render.
|
|
26
|
+
// This ensures stale positions don't persist after component unmount.
|
|
27
|
+
const activeCursor = getActiveCursor();
|
|
28
|
+
cursorDirty = false;
|
|
29
|
+
const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
|
|
30
|
+
if (!hasChanges(str, activeCursor)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const lines = str.split('\n');
|
|
34
|
+
const visibleCount = visibleLineCount(lines, str);
|
|
35
|
+
const cursorSuffix = buildCursorSuffix(visibleCount, activeCursor);
|
|
36
|
+
if (str === previousOutput && cursorChanged) {
|
|
37
|
+
stream.write(buildCursorOnlySequence({
|
|
38
|
+
cursorWasShown,
|
|
39
|
+
previousLineCount,
|
|
40
|
+
previousCursorPosition,
|
|
41
|
+
visibleLineCount: visibleCount,
|
|
42
|
+
cursorPosition: activeCursor,
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
previousOutput = str;
|
|
47
|
+
const returnPrefix = buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition);
|
|
48
|
+
stream.write(returnPrefix +
|
|
49
|
+
ansiEscapes.eraseLines(previousLineCount) +
|
|
50
|
+
str +
|
|
51
|
+
cursorSuffix);
|
|
52
|
+
previousLineCount = lines.length;
|
|
15
53
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
54
|
+
previousCursorPosition = activeCursor ? { ...activeCursor } : undefined;
|
|
55
|
+
cursorWasShown = activeCursor !== undefined;
|
|
56
|
+
return true;
|
|
19
57
|
};
|
|
20
58
|
render.clear = () => {
|
|
21
|
-
|
|
59
|
+
const prefix = buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition);
|
|
60
|
+
stream.write(prefix + ansiEscapes.eraseLines(previousLineCount));
|
|
22
61
|
previousOutput = '';
|
|
23
62
|
previousLineCount = 0;
|
|
63
|
+
previousCursorPosition = undefined;
|
|
64
|
+
cursorWasShown = false;
|
|
24
65
|
};
|
|
25
66
|
render.done = () => {
|
|
26
67
|
previousOutput = '';
|
|
27
68
|
previousLineCount = 0;
|
|
69
|
+
previousCursorPosition = undefined;
|
|
70
|
+
cursorWasShown = false;
|
|
28
71
|
if (!showCursor) {
|
|
29
|
-
cliCursor.show();
|
|
72
|
+
cliCursor.show(stream);
|
|
30
73
|
hasHiddenCursor = false;
|
|
31
74
|
}
|
|
32
75
|
};
|
|
33
76
|
render.sync = (str) => {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
77
|
+
const activeCursor = cursorDirty ? cursorPosition : undefined;
|
|
78
|
+
cursorDirty = false;
|
|
79
|
+
const lines = str.split('\n');
|
|
80
|
+
previousOutput = str;
|
|
81
|
+
previousLineCount = lines.length;
|
|
82
|
+
if (!activeCursor && cursorWasShown) {
|
|
83
|
+
stream.write(hideCursorEscape);
|
|
84
|
+
}
|
|
85
|
+
if (activeCursor) {
|
|
86
|
+
stream.write(buildCursorSuffix(visibleLineCount(lines, str), activeCursor));
|
|
87
|
+
}
|
|
88
|
+
previousCursorPosition = activeCursor ? { ...activeCursor } : undefined;
|
|
89
|
+
cursorWasShown = activeCursor !== undefined;
|
|
90
|
+
};
|
|
91
|
+
render.setCursorPosition = (position) => {
|
|
92
|
+
cursorPosition = position;
|
|
93
|
+
cursorDirty = true;
|
|
37
94
|
};
|
|
95
|
+
render.isCursorDirty = () => cursorDirty;
|
|
96
|
+
render.willRender = (str) => hasChanges(str, getActiveCursor());
|
|
38
97
|
return render;
|
|
39
98
|
};
|
|
40
99
|
const createIncremental = (stream, { showCursor = false } = {}) => {
|
|
41
100
|
let previousLines = [];
|
|
42
101
|
let previousOutput = '';
|
|
43
102
|
let hasHiddenCursor = false;
|
|
103
|
+
let cursorPosition;
|
|
104
|
+
let cursorDirty = false;
|
|
105
|
+
let previousCursorPosition;
|
|
106
|
+
let cursorWasShown = false;
|
|
107
|
+
const getActiveCursor = () => (cursorDirty ? cursorPosition : undefined);
|
|
108
|
+
const hasChanges = (str, activeCursor) => {
|
|
109
|
+
const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
|
|
110
|
+
return str !== previousOutput || cursorChanged;
|
|
111
|
+
};
|
|
44
112
|
const render = (str) => {
|
|
45
113
|
if (!showCursor && !hasHiddenCursor) {
|
|
46
|
-
cliCursor.hide();
|
|
114
|
+
cliCursor.hide(stream);
|
|
47
115
|
hasHiddenCursor = true;
|
|
48
116
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
117
|
+
// Only use cursor if setCursorPosition was called since last render.
|
|
118
|
+
// This ensures stale positions don't persist after component unmount.
|
|
119
|
+
const activeCursor = getActiveCursor();
|
|
120
|
+
cursorDirty = false;
|
|
121
|
+
const cursorChanged = cursorPositionChanged(activeCursor, previousCursorPosition);
|
|
122
|
+
if (!hasChanges(str, activeCursor)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const nextLines = str.split('\n');
|
|
126
|
+
const visibleCount = visibleLineCount(nextLines, str);
|
|
127
|
+
const previousVisible = visibleLineCount(previousLines, previousOutput);
|
|
128
|
+
if (str === previousOutput && cursorChanged) {
|
|
129
|
+
stream.write(buildCursorOnlySequence({
|
|
130
|
+
cursorWasShown,
|
|
131
|
+
previousLineCount: previousLines.length,
|
|
132
|
+
previousCursorPosition,
|
|
133
|
+
visibleLineCount: visibleCount,
|
|
134
|
+
cursorPosition: activeCursor,
|
|
135
|
+
}));
|
|
136
|
+
previousCursorPosition = activeCursor ? { ...activeCursor } : undefined;
|
|
137
|
+
cursorWasShown = activeCursor !== undefined;
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
const returnPrefix = buildReturnToBottomPrefix(cursorWasShown, previousLines.length, previousCursorPosition);
|
|
141
|
+
if (str === '\n' || previousOutput.length === 0) {
|
|
142
|
+
const cursorSuffix = buildCursorSuffix(visibleCount, activeCursor);
|
|
143
|
+
stream.write(returnPrefix +
|
|
144
|
+
ansiEscapes.eraseLines(previousLines.length) +
|
|
145
|
+
str +
|
|
146
|
+
cursorSuffix);
|
|
147
|
+
cursorWasShown = activeCursor !== undefined;
|
|
148
|
+
previousCursorPosition = activeCursor ? { ...activeCursor } : undefined;
|
|
149
|
+
previousOutput = str;
|
|
60
150
|
previousLines = nextLines;
|
|
61
|
-
return;
|
|
151
|
+
return true;
|
|
62
152
|
}
|
|
153
|
+
const hasTrailingNewline = str.endsWith('\n');
|
|
63
154
|
// We aggregate all chunks for incremental rendering into a buffer, and then write them to stdout at the end.
|
|
64
155
|
const buffer = [];
|
|
156
|
+
buffer.push(returnPrefix);
|
|
65
157
|
// Clear extra lines if the current content's line count is lower than the previous.
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
ansiEscapes.eraseLines(
|
|
70
|
-
// Positions cursor to the top of the rendered output.
|
|
71
|
-
ansiEscapes.cursorUp(visibleCount));
|
|
158
|
+
if (visibleCount < previousVisible) {
|
|
159
|
+
const previousHadTrailingNewline = previousOutput.endsWith('\n');
|
|
160
|
+
const extraSlot = previousHadTrailingNewline ? 1 : 0;
|
|
161
|
+
buffer.push(ansiEscapes.eraseLines(previousVisible - visibleCount + extraSlot), ansiEscapes.cursorUp(visibleCount));
|
|
72
162
|
}
|
|
73
163
|
else {
|
|
74
|
-
buffer.push(ansiEscapes.cursorUp(
|
|
164
|
+
buffer.push(ansiEscapes.cursorUp(previousVisible - 1));
|
|
75
165
|
}
|
|
76
166
|
for (let i = 0; i < visibleCount; i++) {
|
|
167
|
+
const isLastLine = i === visibleCount - 1;
|
|
77
168
|
// We do not write lines if the contents are the same. This prevents flickering during renders.
|
|
78
169
|
if (nextLines[i] === previousLines[i]) {
|
|
79
|
-
|
|
170
|
+
// Don't move past the last line when there's no trailing newline,
|
|
171
|
+
// otherwise the cursor overshoots the rendered block.
|
|
172
|
+
if (!isLastLine || hasTrailingNewline) {
|
|
173
|
+
buffer.push(ansiEscapes.cursorNextLine);
|
|
174
|
+
}
|
|
80
175
|
continue;
|
|
81
176
|
}
|
|
82
|
-
buffer.push(ansiEscapes.
|
|
177
|
+
buffer.push(ansiEscapes.cursorTo(0) +
|
|
178
|
+
nextLines[i] +
|
|
179
|
+
ansiEscapes.eraseEndLine +
|
|
180
|
+
// Don't append newline after the last line when the input
|
|
181
|
+
// has no trailing newline (fullscreen mode).
|
|
182
|
+
(isLastLine && !hasTrailingNewline ? '' : '\n'));
|
|
83
183
|
}
|
|
184
|
+
const cursorSuffix = buildCursorSuffix(visibleCount, activeCursor);
|
|
185
|
+
buffer.push(cursorSuffix);
|
|
84
186
|
stream.write(buffer.join(''));
|
|
85
|
-
|
|
187
|
+
cursorWasShown = activeCursor !== undefined;
|
|
188
|
+
previousCursorPosition = activeCursor ? { ...activeCursor } : undefined;
|
|
189
|
+
previousOutput = str;
|
|
86
190
|
previousLines = nextLines;
|
|
191
|
+
return true;
|
|
87
192
|
};
|
|
88
193
|
render.clear = () => {
|
|
89
|
-
|
|
194
|
+
const prefix = buildReturnToBottomPrefix(cursorWasShown, previousLines.length, previousCursorPosition);
|
|
195
|
+
stream.write(prefix + ansiEscapes.eraseLines(previousLines.length));
|
|
90
196
|
previousOutput = '';
|
|
91
197
|
previousLines = [];
|
|
198
|
+
previousCursorPosition = undefined;
|
|
199
|
+
cursorWasShown = false;
|
|
92
200
|
};
|
|
93
201
|
render.done = () => {
|
|
94
202
|
previousOutput = '';
|
|
95
203
|
previousLines = [];
|
|
204
|
+
previousCursorPosition = undefined;
|
|
205
|
+
cursorWasShown = false;
|
|
96
206
|
if (!showCursor) {
|
|
97
|
-
cliCursor.show();
|
|
207
|
+
cliCursor.show(stream);
|
|
98
208
|
hasHiddenCursor = false;
|
|
99
209
|
}
|
|
100
210
|
};
|
|
101
211
|
render.sync = (str) => {
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
212
|
+
const activeCursor = cursorDirty ? cursorPosition : undefined;
|
|
213
|
+
cursorDirty = false;
|
|
214
|
+
const lines = str.split('\n');
|
|
215
|
+
previousOutput = str;
|
|
216
|
+
previousLines = lines;
|
|
217
|
+
if (!activeCursor && cursorWasShown) {
|
|
218
|
+
stream.write(hideCursorEscape);
|
|
219
|
+
}
|
|
220
|
+
if (activeCursor) {
|
|
221
|
+
stream.write(buildCursorSuffix(visibleLineCount(lines, str), activeCursor));
|
|
222
|
+
}
|
|
223
|
+
previousCursorPosition = activeCursor ? { ...activeCursor } : undefined;
|
|
224
|
+
cursorWasShown = activeCursor !== undefined;
|
|
225
|
+
};
|
|
226
|
+
render.setCursorPosition = (position) => {
|
|
227
|
+
cursorPosition = position;
|
|
228
|
+
cursorDirty = true;
|
|
105
229
|
};
|
|
230
|
+
render.isCursorDirty = () => cursorDirty;
|
|
231
|
+
render.willRender = (str) => hasChanges(str, getActiveCursor());
|
|
106
232
|
return render;
|
|
107
233
|
};
|
|
108
234
|
const create = (stream, { showCursor = false, incremental = false } = {}) => {
|
package/build/log-update.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-update.js","sourceRoot":"","sources":["../src/log-update.ts"],"names":[],"mappings":"AACA,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,SAAS,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"log-update.js","sourceRoot":"","sources":["../src/log-update.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,14 @@ type ParsedKey = {
|
|
|
9
9
|
sequence: string;
|
|
10
10
|
raw: string | undefined;
|
|
11
11
|
code?: string;
|
|
12
|
+
super?: boolean;
|
|
13
|
+
hyper?: boolean;
|
|
14
|
+
capsLock?: boolean;
|
|
15
|
+
numLock?: boolean;
|
|
16
|
+
eventType?: 'press' | 'repeat' | 'release';
|
|
17
|
+
isKittyProtocol?: boolean;
|
|
18
|
+
text?: string;
|
|
19
|
+
isPrintable?: boolean;
|
|
12
20
|
};
|
|
13
21
|
declare const parseKeypress: (s?: Buffer | string) => ParsedKey;
|
|
14
22
|
export default parseKeypress;
|
package/build/parse-keypress.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Copied from https://github.com/enquirer/enquirer/blob/36785f3399a41cd61e9d28d1eb9c2fcd73d69b4c/lib/keypress.js
|
|
2
2
|
import { Buffer } from 'node:buffer';
|
|
3
|
+
import { kittyModifiers } from './kitty-keyboard.js';
|
|
3
4
|
const metaKeyCodeRe = /^(?:\x1b)([a-zA-Z0-9])$/;
|
|
4
5
|
const fnKeyRe = /^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
|
|
5
6
|
const keyName = {
|
|
@@ -115,6 +116,249 @@ const isCtrlKey = (code) => {
|
|
|
115
116
|
'[8^',
|
|
116
117
|
].includes(code);
|
|
117
118
|
};
|
|
119
|
+
// Kitty keyboard protocol: CSI codepoint ; modifiers [: eventType] [; text-as-codepoints] u
|
|
120
|
+
const kittyKeyRe = /^\x1b\[(\d+)(?:;(\d+)(?::(\d+))?(?:;([\d:]+))?)?u$/;
|
|
121
|
+
// Kitty-enhanced special keys: CSI number ; modifiers : eventType {letter|~}
|
|
122
|
+
// These are legacy CSI sequences enhanced with the :eventType field.
|
|
123
|
+
// Examples: \x1b[1;1:1A (up arrow press), \x1b[3;1:3~ (delete release)
|
|
124
|
+
const kittySpecialKeyRe = /^\x1b\[(\d+);(\d+):(\d+)([A-Za-z~])$/;
|
|
125
|
+
// Letter-terminated special key names (CSI 1 ; mods letter)
|
|
126
|
+
const kittySpecialLetterKeys = {
|
|
127
|
+
A: 'up',
|
|
128
|
+
B: 'down',
|
|
129
|
+
C: 'right',
|
|
130
|
+
D: 'left',
|
|
131
|
+
E: 'clear',
|
|
132
|
+
F: 'end',
|
|
133
|
+
H: 'home',
|
|
134
|
+
P: 'f1',
|
|
135
|
+
Q: 'f2',
|
|
136
|
+
R: 'f3',
|
|
137
|
+
S: 'f4',
|
|
138
|
+
};
|
|
139
|
+
// Number-terminated special key names (CSI number ; mods ~)
|
|
140
|
+
const kittySpecialNumberKeys = {
|
|
141
|
+
2: 'insert',
|
|
142
|
+
3: 'delete',
|
|
143
|
+
5: 'pageup',
|
|
144
|
+
6: 'pagedown',
|
|
145
|
+
7: 'home',
|
|
146
|
+
8: 'end',
|
|
147
|
+
11: 'f1',
|
|
148
|
+
12: 'f2',
|
|
149
|
+
13: 'f3',
|
|
150
|
+
14: 'f4',
|
|
151
|
+
15: 'f5',
|
|
152
|
+
17: 'f6',
|
|
153
|
+
18: 'f7',
|
|
154
|
+
19: 'f8',
|
|
155
|
+
20: 'f9',
|
|
156
|
+
21: 'f10',
|
|
157
|
+
23: 'f11',
|
|
158
|
+
24: 'f12',
|
|
159
|
+
};
|
|
160
|
+
// Map of special codepoints to key names in kitty protocol
|
|
161
|
+
const kittyCodepointNames = {
|
|
162
|
+
27: 'escape',
|
|
163
|
+
// 13 (return) and 32 (space) are handled before this lookup
|
|
164
|
+
// in parseKittyKeypress so they can be marked as printable.
|
|
165
|
+
9: 'tab',
|
|
166
|
+
127: 'delete',
|
|
167
|
+
8: 'backspace',
|
|
168
|
+
57358: 'capslock',
|
|
169
|
+
57359: 'scrolllock',
|
|
170
|
+
57360: 'numlock',
|
|
171
|
+
57361: 'printscreen',
|
|
172
|
+
57362: 'pause',
|
|
173
|
+
57363: 'menu',
|
|
174
|
+
57376: 'f13',
|
|
175
|
+
57377: 'f14',
|
|
176
|
+
57378: 'f15',
|
|
177
|
+
57379: 'f16',
|
|
178
|
+
57380: 'f17',
|
|
179
|
+
57381: 'f18',
|
|
180
|
+
57382: 'f19',
|
|
181
|
+
57383: 'f20',
|
|
182
|
+
57384: 'f21',
|
|
183
|
+
57385: 'f22',
|
|
184
|
+
57386: 'f23',
|
|
185
|
+
57387: 'f24',
|
|
186
|
+
57388: 'f25',
|
|
187
|
+
57389: 'f26',
|
|
188
|
+
57390: 'f27',
|
|
189
|
+
57391: 'f28',
|
|
190
|
+
57392: 'f29',
|
|
191
|
+
57393: 'f30',
|
|
192
|
+
57394: 'f31',
|
|
193
|
+
57395: 'f32',
|
|
194
|
+
57396: 'f33',
|
|
195
|
+
57397: 'f34',
|
|
196
|
+
57398: 'f35',
|
|
197
|
+
57399: 'kp0',
|
|
198
|
+
57400: 'kp1',
|
|
199
|
+
57401: 'kp2',
|
|
200
|
+
57402: 'kp3',
|
|
201
|
+
57403: 'kp4',
|
|
202
|
+
57404: 'kp5',
|
|
203
|
+
57405: 'kp6',
|
|
204
|
+
57406: 'kp7',
|
|
205
|
+
57407: 'kp8',
|
|
206
|
+
57408: 'kp9',
|
|
207
|
+
57409: 'kpdecimal',
|
|
208
|
+
57410: 'kpdivide',
|
|
209
|
+
57411: 'kpmultiply',
|
|
210
|
+
57412: 'kpsubtract',
|
|
211
|
+
57413: 'kpadd',
|
|
212
|
+
57414: 'kpenter',
|
|
213
|
+
57415: 'kpequal',
|
|
214
|
+
57416: 'kpseparator',
|
|
215
|
+
57417: 'kpleft',
|
|
216
|
+
57418: 'kpright',
|
|
217
|
+
57419: 'kpup',
|
|
218
|
+
57420: 'kpdown',
|
|
219
|
+
57421: 'kppageup',
|
|
220
|
+
57422: 'kppagedown',
|
|
221
|
+
57423: 'kphome',
|
|
222
|
+
57424: 'kpend',
|
|
223
|
+
57425: 'kpinsert',
|
|
224
|
+
57426: 'kpdelete',
|
|
225
|
+
57427: 'kpbegin',
|
|
226
|
+
57428: 'mediaplay',
|
|
227
|
+
57429: 'mediapause',
|
|
228
|
+
57430: 'mediaplaypause',
|
|
229
|
+
57431: 'mediareverse',
|
|
230
|
+
57432: 'mediastop',
|
|
231
|
+
57433: 'mediafastforward',
|
|
232
|
+
57434: 'mediarewind',
|
|
233
|
+
57435: 'mediatracknext',
|
|
234
|
+
57436: 'mediatrackprevious',
|
|
235
|
+
57437: 'mediarecord',
|
|
236
|
+
57438: 'lowervolume',
|
|
237
|
+
57439: 'raisevolume',
|
|
238
|
+
57440: 'mutevolume',
|
|
239
|
+
57441: 'leftshift',
|
|
240
|
+
57442: 'leftcontrol',
|
|
241
|
+
57443: 'leftalt',
|
|
242
|
+
57444: 'leftsuper',
|
|
243
|
+
57445: 'lefthyper',
|
|
244
|
+
57446: 'leftmeta',
|
|
245
|
+
57447: 'rightshift',
|
|
246
|
+
57448: 'rightcontrol',
|
|
247
|
+
57449: 'rightalt',
|
|
248
|
+
57450: 'rightsuper',
|
|
249
|
+
57451: 'righthyper',
|
|
250
|
+
57452: 'rightmeta',
|
|
251
|
+
57453: 'isoLevel3Shift',
|
|
252
|
+
57454: 'isoLevel5Shift',
|
|
253
|
+
};
|
|
254
|
+
// Valid Unicode codepoint range, excluding surrogates
|
|
255
|
+
const isValidCodepoint = (cp) => cp >= 0 && cp <= 0x10_ffff && !(cp >= 0xd8_00 && cp <= 0xdf_ff);
|
|
256
|
+
const safeFromCodePoint = (cp) => isValidCodepoint(cp) ? String.fromCodePoint(cp) : '?';
|
|
257
|
+
function resolveEventType(value) {
|
|
258
|
+
if (value === 3)
|
|
259
|
+
return 'release';
|
|
260
|
+
if (value === 2)
|
|
261
|
+
return 'repeat';
|
|
262
|
+
return 'press';
|
|
263
|
+
}
|
|
264
|
+
function parseKittyModifiers(modifiers) {
|
|
265
|
+
return {
|
|
266
|
+
ctrl: !!(modifiers & kittyModifiers.ctrl),
|
|
267
|
+
shift: !!(modifiers & kittyModifiers.shift),
|
|
268
|
+
meta: !!(modifiers & kittyModifiers.meta),
|
|
269
|
+
option: !!(modifiers & kittyModifiers.alt),
|
|
270
|
+
super: !!(modifiers & kittyModifiers.super),
|
|
271
|
+
hyper: !!(modifiers & kittyModifiers.hyper),
|
|
272
|
+
capsLock: !!(modifiers & kittyModifiers.capsLock),
|
|
273
|
+
numLock: !!(modifiers & kittyModifiers.numLock),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const parseKittyKeypress = (s) => {
|
|
277
|
+
const match = kittyKeyRe.exec(s);
|
|
278
|
+
if (!match)
|
|
279
|
+
return null;
|
|
280
|
+
const codepoint = parseInt(match[1], 10);
|
|
281
|
+
const modifiers = match[2] ? Math.max(0, parseInt(match[2], 10) - 1) : 0;
|
|
282
|
+
const eventType = match[3] ? parseInt(match[3], 10) : 1;
|
|
283
|
+
const textField = match[4];
|
|
284
|
+
// Bail on invalid primary codepoint
|
|
285
|
+
if (!isValidCodepoint(codepoint)) {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
// Parse text-as-codepoints field (colon-separated Unicode codepoints)
|
|
289
|
+
let text;
|
|
290
|
+
if (textField) {
|
|
291
|
+
text = textField
|
|
292
|
+
.split(':')
|
|
293
|
+
.map(cp => safeFromCodePoint(parseInt(cp, 10)))
|
|
294
|
+
.join('');
|
|
295
|
+
}
|
|
296
|
+
// Determine key name from codepoint
|
|
297
|
+
let name;
|
|
298
|
+
let isPrintable;
|
|
299
|
+
if (codepoint === 32) {
|
|
300
|
+
name = 'space';
|
|
301
|
+
isPrintable = true;
|
|
302
|
+
}
|
|
303
|
+
else if (codepoint === 13) {
|
|
304
|
+
name = 'return';
|
|
305
|
+
isPrintable = true;
|
|
306
|
+
}
|
|
307
|
+
else if (kittyCodepointNames[codepoint]) {
|
|
308
|
+
name = kittyCodepointNames[codepoint];
|
|
309
|
+
isPrintable = false;
|
|
310
|
+
}
|
|
311
|
+
else if (codepoint >= 1 && codepoint <= 26) {
|
|
312
|
+
// Ctrl+letter comes as codepoint 1-26
|
|
313
|
+
name = String.fromCodePoint(codepoint + 96); // 'a' is 97
|
|
314
|
+
isPrintable = false;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
name = safeFromCodePoint(codepoint).toLowerCase();
|
|
318
|
+
isPrintable = true;
|
|
319
|
+
}
|
|
320
|
+
// Default text to the character from the codepoint when not explicitly
|
|
321
|
+
// provided by the protocol, so keys like space and return produce their
|
|
322
|
+
// expected text input (' ' and '\r' respectively).
|
|
323
|
+
if (isPrintable && !text) {
|
|
324
|
+
text = safeFromCodePoint(codepoint);
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
name,
|
|
328
|
+
...parseKittyModifiers(modifiers),
|
|
329
|
+
eventType: resolveEventType(eventType),
|
|
330
|
+
sequence: s,
|
|
331
|
+
raw: s,
|
|
332
|
+
isKittyProtocol: true,
|
|
333
|
+
isPrintable,
|
|
334
|
+
text,
|
|
335
|
+
};
|
|
336
|
+
};
|
|
337
|
+
// Parse kitty-enhanced special key sequences (arrow keys, function keys, etc.)
|
|
338
|
+
// These use the legacy CSI format but with an added :eventType field.
|
|
339
|
+
const parseKittySpecialKey = (s) => {
|
|
340
|
+
const match = kittySpecialKeyRe.exec(s);
|
|
341
|
+
if (!match)
|
|
342
|
+
return null;
|
|
343
|
+
const number = parseInt(match[1], 10);
|
|
344
|
+
const modifiers = Math.max(0, parseInt(match[2], 10) - 1);
|
|
345
|
+
const eventType = parseInt(match[3], 10);
|
|
346
|
+
const terminator = match[4];
|
|
347
|
+
const name = terminator === '~'
|
|
348
|
+
? kittySpecialNumberKeys[number]
|
|
349
|
+
: kittySpecialLetterKeys[terminator];
|
|
350
|
+
if (!name)
|
|
351
|
+
return null;
|
|
352
|
+
return {
|
|
353
|
+
name,
|
|
354
|
+
...parseKittyModifiers(modifiers),
|
|
355
|
+
eventType: resolveEventType(eventType),
|
|
356
|
+
sequence: s,
|
|
357
|
+
raw: s,
|
|
358
|
+
isKittyProtocol: true,
|
|
359
|
+
isPrintable: false,
|
|
360
|
+
};
|
|
361
|
+
};
|
|
118
362
|
const parseKeypress = (s = '') => {
|
|
119
363
|
let parts;
|
|
120
364
|
if (Buffer.isBuffer(s)) {
|
|
@@ -132,6 +376,29 @@ const parseKeypress = (s = '') => {
|
|
|
132
376
|
else if (!s) {
|
|
133
377
|
s = '';
|
|
134
378
|
}
|
|
379
|
+
// Try kitty keyboard protocol parsers first
|
|
380
|
+
const kittyResult = parseKittyKeypress(s);
|
|
381
|
+
if (kittyResult)
|
|
382
|
+
return kittyResult;
|
|
383
|
+
const kittySpecialResult = parseKittySpecialKey(s);
|
|
384
|
+
if (kittySpecialResult)
|
|
385
|
+
return kittySpecialResult;
|
|
386
|
+
// If the input matched the kitty CSI-u pattern but was rejected (e.g.,
|
|
387
|
+
// invalid codepoint), return a safe empty keypress instead of falling
|
|
388
|
+
// through to legacy parsing which can produce unsafe states (undefined name)
|
|
389
|
+
if (kittyKeyRe.test(s)) {
|
|
390
|
+
return {
|
|
391
|
+
name: '',
|
|
392
|
+
ctrl: false,
|
|
393
|
+
meta: false,
|
|
394
|
+
shift: false,
|
|
395
|
+
option: false,
|
|
396
|
+
sequence: s,
|
|
397
|
+
raw: s,
|
|
398
|
+
isKittyProtocol: true,
|
|
399
|
+
isPrintable: false,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
135
402
|
const key = {
|
|
136
403
|
name: '',
|
|
137
404
|
ctrl: false,
|
|
@@ -142,10 +409,11 @@ const parseKeypress = (s = '') => {
|
|
|
142
409
|
raw: s,
|
|
143
410
|
};
|
|
144
411
|
key.sequence = key.sequence || s || key.name;
|
|
145
|
-
if (s === '\r') {
|
|
146
|
-
// carriage return
|
|
412
|
+
if (s === '\r' || s === '\x1b\r') {
|
|
413
|
+
// carriage return (or option+return on macOS)
|
|
147
414
|
key.raw = undefined;
|
|
148
415
|
key.name = 'return';
|
|
416
|
+
key.option = s.length === 2;
|
|
149
417
|
}
|
|
150
418
|
else if (s === '\n') {
|
|
151
419
|
// enter, should have been called linefeed
|