@levu/snap 0.3.7 → 0.3.8
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.8] - 2026-02-26
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Prevented premature submit during non-bracketed multi-line paste bursts where newline Enter events were treated as user submit before all pasted lines arrived.
|
|
12
|
+
- Added explicit paste-burst detection from raw input stream and suppressed Enter-submit only during the short paste window.
|
|
13
|
+
- Kept bracketed paste behavior intact and avoided false suppression after paste-end marker.
|
|
14
|
+
- Added regression coverage for non-bracketed multi-line paste with delayed second-line arrival.
|
|
15
|
+
|
|
8
16
|
## [0.3.7] - 2026-02-26
|
|
9
17
|
|
|
10
18
|
### Fixed
|
|
@@ -35,6 +35,7 @@ export const createMultilineTextPrompt = () => {
|
|
|
35
35
|
let bracketPasteProbe = '';
|
|
36
36
|
let pendingEnterSubmit = false;
|
|
37
37
|
let pendingEnterSubmitTimer;
|
|
38
|
+
let recentPasteBurstUntil = 0;
|
|
38
39
|
const cleanup = () => {
|
|
39
40
|
if (pendingEnterSubmitTimer) {
|
|
40
41
|
clearTimeout(pendingEnterSubmitTimer);
|
|
@@ -141,6 +142,7 @@ export const createMultilineTextPrompt = () => {
|
|
|
141
142
|
const BRACKET_PASTE_START = '\u001b[200~';
|
|
142
143
|
const BRACKET_PASTE_END = '\u001b[201~';
|
|
143
144
|
const BRACKET_PASTE_PROBE_MAX = 96;
|
|
145
|
+
const PASTE_BURST_WINDOW_MS = 70;
|
|
144
146
|
const updateBracketPasteState = (chunk) => {
|
|
145
147
|
if (!chunk)
|
|
146
148
|
return;
|
|
@@ -160,6 +162,20 @@ export const createMultilineTextPrompt = () => {
|
|
|
160
162
|
}
|
|
161
163
|
}
|
|
162
164
|
};
|
|
165
|
+
const notePossiblePasteBurst = (chunk) => {
|
|
166
|
+
if (!chunk)
|
|
167
|
+
return;
|
|
168
|
+
const normalized = chunk
|
|
169
|
+
.replaceAll(BRACKET_PASTE_START, '')
|
|
170
|
+
.replaceAll(BRACKET_PASTE_END, '');
|
|
171
|
+
if (!normalized)
|
|
172
|
+
return;
|
|
173
|
+
// In raw mode, normal typing usually arrives as single-byte chunks.
|
|
174
|
+
// Multi-byte chunks and newlines are strong signals that input is a paste burst.
|
|
175
|
+
if (normalized.length > 1 || /[\r\n]/.test(normalized)) {
|
|
176
|
+
recentPasteBurstUntil = Math.max(recentPasteBurstUntil, Date.now() + PASTE_BURST_WINDOW_MS);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
163
179
|
showPrompt();
|
|
164
180
|
// Handle paste from clipboard
|
|
165
181
|
const handlePaste = async () => {
|
|
@@ -200,12 +216,19 @@ export const createMultilineTextPrompt = () => {
|
|
|
200
216
|
rl.on('line', (line) => {
|
|
201
217
|
if (cancelled)
|
|
202
218
|
return;
|
|
219
|
+
const now = Date.now();
|
|
203
220
|
if (pendingEnterSubmit) {
|
|
221
|
+
const likelyPasteFlow = now < recentPasteBurstUntil;
|
|
204
222
|
pendingEnterSubmit = false;
|
|
205
223
|
if (pendingEnterSubmitTimer) {
|
|
206
224
|
clearTimeout(pendingEnterSubmitTimer);
|
|
207
225
|
pendingEnterSubmitTimer = undefined;
|
|
208
226
|
}
|
|
227
|
+
if (likelyPasteFlow) {
|
|
228
|
+
absorbLine(line);
|
|
229
|
+
showPrompt();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
209
232
|
absorbLine(line);
|
|
210
233
|
submit(buildSubmitValue());
|
|
211
234
|
return;
|
|
@@ -231,7 +254,6 @@ export const createMultilineTextPrompt = () => {
|
|
|
231
254
|
showPrompt();
|
|
232
255
|
return;
|
|
233
256
|
}
|
|
234
|
-
const now = Date.now();
|
|
235
257
|
// Check for double Enter to submit
|
|
236
258
|
if (line === '' && now - lastEnterTime < DOUBLE_ENTER_TIMEOUT) {
|
|
237
259
|
submit(buildSubmitValue());
|
|
@@ -262,6 +284,7 @@ export const createMultilineTextPrompt = () => {
|
|
|
262
284
|
return;
|
|
263
285
|
const text = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk ?? '');
|
|
264
286
|
updateBracketPasteState(text);
|
|
287
|
+
notePossiblePasteBurst(text);
|
|
265
288
|
};
|
|
266
289
|
input.on('data', dataListener);
|
|
267
290
|
keypressListener = async (str, key) => {
|
|
@@ -319,6 +342,11 @@ export const createMultilineTextPrompt = () => {
|
|
|
319
342
|
insertNewline();
|
|
320
343
|
}
|
|
321
344
|
else if (key.name === 'enter' || key.name === 'return') {
|
|
345
|
+
const now = Date.now();
|
|
346
|
+
const likelyPasteReturn = now < recentPasteBurstUntil;
|
|
347
|
+
if (likelyPasteReturn) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
322
350
|
if (key.shift || key.alt) {
|
|
323
351
|
ignoreNextLineEvent = true;
|
|
324
352
|
// Shift+Enter / Alt+Enter inserts a new line.
|