@levu/snap 0.3.6 → 0.3.7
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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.7] - 2026-02-26
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Fixed Enter-submit race in multiline prompt where pasted multi-line input could lose the final line and submit only the first line.
|
|
12
|
+
- Enter submit now waits briefly for the corresponding readline `line` event (with fallback timeout), preserving pending paste buffer content.
|
|
13
|
+
- Prompt rendering now prefers live readline buffer content so pending pasted lines are visible before submit.
|
|
14
|
+
- Added regression coverage for the “first line only after multiline paste” scenario.
|
|
15
|
+
|
|
8
16
|
## [0.3.6] - 2026-02-26
|
|
9
17
|
|
|
10
18
|
### Fixed
|
|
@@ -33,7 +33,13 @@ export const createMultilineTextPrompt = () => {
|
|
|
33
33
|
let expectingGhosttySequenceEcho = false;
|
|
34
34
|
let bracketPasteActive = false;
|
|
35
35
|
let bracketPasteProbe = '';
|
|
36
|
+
let pendingEnterSubmit = false;
|
|
37
|
+
let pendingEnterSubmitTimer;
|
|
36
38
|
const cleanup = () => {
|
|
39
|
+
if (pendingEnterSubmitTimer) {
|
|
40
|
+
clearTimeout(pendingEnterSubmitTimer);
|
|
41
|
+
pendingEnterSubmitTimer = undefined;
|
|
42
|
+
}
|
|
37
43
|
if (keypressListener) {
|
|
38
44
|
input.off('keypress', keypressListener);
|
|
39
45
|
}
|
|
@@ -110,6 +116,19 @@ export const createMultilineTextPrompt = () => {
|
|
|
110
116
|
const buildSubmitValue = () => {
|
|
111
117
|
return normalizeGhosttyInlineTokens(lines.concat(getLiveLine()).join('\n'));
|
|
112
118
|
};
|
|
119
|
+
const absorbLine = (line) => {
|
|
120
|
+
if (line.trim() === '') {
|
|
121
|
+
if (currentLine !== '') {
|
|
122
|
+
lines.push(currentLine);
|
|
123
|
+
currentLine = '';
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (currentLine !== '') {
|
|
128
|
+
lines.push(currentLine);
|
|
129
|
+
}
|
|
130
|
+
currentLine = line;
|
|
131
|
+
};
|
|
113
132
|
const insertNewline = () => {
|
|
114
133
|
lines.push(getLiveLine());
|
|
115
134
|
currentLine = '';
|
|
@@ -117,7 +136,7 @@ export const createMultilineTextPrompt = () => {
|
|
|
117
136
|
output.write(`\n${pc.dim('> ')}`);
|
|
118
137
|
};
|
|
119
138
|
const showPrompt = () => {
|
|
120
|
-
output.write(`\n${pc.dim('> ')}${
|
|
139
|
+
output.write(`\n${pc.dim('> ')}${getLiveLine()}`);
|
|
121
140
|
};
|
|
122
141
|
const BRACKET_PASTE_START = '\u001b[200~';
|
|
123
142
|
const BRACKET_PASTE_END = '\u001b[201~';
|
|
@@ -181,6 +200,16 @@ export const createMultilineTextPrompt = () => {
|
|
|
181
200
|
rl.on('line', (line) => {
|
|
182
201
|
if (cancelled)
|
|
183
202
|
return;
|
|
203
|
+
if (pendingEnterSubmit) {
|
|
204
|
+
pendingEnterSubmit = false;
|
|
205
|
+
if (pendingEnterSubmitTimer) {
|
|
206
|
+
clearTimeout(pendingEnterSubmitTimer);
|
|
207
|
+
pendingEnterSubmitTimer = undefined;
|
|
208
|
+
}
|
|
209
|
+
absorbLine(line);
|
|
210
|
+
submit(buildSubmitValue());
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
184
213
|
if (ignoreNextLineEvent) {
|
|
185
214
|
ignoreNextLineEvent = false;
|
|
186
215
|
return;
|
|
@@ -209,20 +238,7 @@ export const createMultilineTextPrompt = () => {
|
|
|
209
238
|
return;
|
|
210
239
|
}
|
|
211
240
|
lastEnterTime = now;
|
|
212
|
-
|
|
213
|
-
// Empty line - add to lines
|
|
214
|
-
if (currentLine !== '') {
|
|
215
|
-
lines.push(currentLine);
|
|
216
|
-
currentLine = '';
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
// Non-empty line
|
|
221
|
-
if (currentLine !== '') {
|
|
222
|
-
lines.push(currentLine);
|
|
223
|
-
}
|
|
224
|
-
currentLine = line;
|
|
225
|
-
}
|
|
241
|
+
absorbLine(line);
|
|
226
242
|
showPrompt();
|
|
227
243
|
});
|
|
228
244
|
// Handle SIGINT (Ctrl+C)
|
|
@@ -303,13 +319,23 @@ export const createMultilineTextPrompt = () => {
|
|
|
303
319
|
insertNewline();
|
|
304
320
|
}
|
|
305
321
|
else if (key.name === 'enter' || key.name === 'return') {
|
|
306
|
-
ignoreNextLineEvent = true;
|
|
307
322
|
if (key.shift || key.alt) {
|
|
323
|
+
ignoreNextLineEvent = true;
|
|
308
324
|
// Shift+Enter / Alt+Enter inserts a new line.
|
|
309
325
|
insertNewline();
|
|
310
326
|
return;
|
|
311
327
|
}
|
|
312
|
-
|
|
328
|
+
pendingEnterSubmit = true;
|
|
329
|
+
if (pendingEnterSubmitTimer) {
|
|
330
|
+
clearTimeout(pendingEnterSubmitTimer);
|
|
331
|
+
}
|
|
332
|
+
pendingEnterSubmitTimer = setTimeout(() => {
|
|
333
|
+
if (!pendingEnterSubmit || cancelled)
|
|
334
|
+
return;
|
|
335
|
+
pendingEnterSubmit = false;
|
|
336
|
+
pendingEnterSubmitTimer = undefined;
|
|
337
|
+
submit(buildSubmitValue());
|
|
338
|
+
}, 20);
|
|
313
339
|
}
|
|
314
340
|
};
|
|
315
341
|
input.on('keypress', keypressListener);
|