@levu/snap 0.3.10 → 0.3.12
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,20 @@ 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.12] - 2026-03-04
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Added missing runtime dependency `picocolors` to prevent `ERR_MODULE_NOT_FOUND` when consuming password/multiline adapters from published packages.
|
|
12
|
+
- Improved password prompt stability with a raw-mode terminal path to reduce cursor/glyph jitter in interactive input.
|
|
13
|
+
- Added password adapter regression tests and updated component reference keyboard behavior notes.
|
|
14
|
+
|
|
15
|
+
## [0.3.11] - 2026-02-28
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Added direct handling of bracketed paste payloads from raw input stream to preserve multiline content in Ghostty-like terminals.
|
|
19
|
+
- Kept compatibility with readline `line`-event paste flow while avoiding duplicate line ingestion when raw bracketed payload is available.
|
|
20
|
+
- Added regression coverage for bracketed paste payload capture without `line` events.
|
|
21
|
+
|
|
8
22
|
## [0.3.10] - 2026-02-28
|
|
9
23
|
|
|
10
24
|
### Fixed
|
|
@@ -33,6 +33,9 @@ export const createMultilineTextPrompt = () => {
|
|
|
33
33
|
let expectingGhosttySequenceEcho = false;
|
|
34
34
|
let bracketPasteActive = false;
|
|
35
35
|
let bracketPasteProbe = '';
|
|
36
|
+
let bracketPasteBuffer = '';
|
|
37
|
+
let bracketPasteHasRawPayload = false;
|
|
38
|
+
let suppressLineEventsUntil = 0;
|
|
36
39
|
let pendingEnterSubmit = false;
|
|
37
40
|
let pendingEnterSubmitTimer;
|
|
38
41
|
let recentPasteBurstUntil = 0;
|
|
@@ -175,6 +178,21 @@ export const createMultilineTextPrompt = () => {
|
|
|
175
178
|
const showPrompt = () => {
|
|
176
179
|
output.write(`\n${pc.dim('> ')}${getLiveLine()}`);
|
|
177
180
|
};
|
|
181
|
+
const applyPastedText = (rawPasted) => {
|
|
182
|
+
const normalized = String(rawPasted || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
183
|
+
if (!normalized)
|
|
184
|
+
return;
|
|
185
|
+
const merged = `${getLiveLine()}${normalized}`;
|
|
186
|
+
const pastedLines = merged.split('\n');
|
|
187
|
+
if (pastedLines.length > 1) {
|
|
188
|
+
lines.push(...pastedLines.slice(0, -1));
|
|
189
|
+
currentLine = pastedLines[pastedLines.length - 1];
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
currentLine = merged;
|
|
193
|
+
}
|
|
194
|
+
rl.line = currentLine;
|
|
195
|
+
};
|
|
178
196
|
const BRACKET_PASTE_START = '\u001b[200~';
|
|
179
197
|
const BRACKET_PASTE_END = '\u001b[201~';
|
|
180
198
|
const BRACKET_PASTE_PROBE_MAX = 96;
|
|
@@ -215,6 +233,38 @@ export const createMultilineTextPrompt = () => {
|
|
|
215
233
|
recentPasteBurstUntil = Math.max(recentPasteBurstUntil, Date.now() + PASTE_BURST_WINDOW_MS);
|
|
216
234
|
}
|
|
217
235
|
};
|
|
236
|
+
const ingestBracketedPasteChunk = (chunk) => {
|
|
237
|
+
if (!chunk)
|
|
238
|
+
return;
|
|
239
|
+
let rest = chunk;
|
|
240
|
+
while (rest.length > 0) {
|
|
241
|
+
if (!bracketPasteActive) {
|
|
242
|
+
const startIndex = rest.indexOf(BRACKET_PASTE_START);
|
|
243
|
+
if (startIndex < 0)
|
|
244
|
+
return;
|
|
245
|
+
bracketPasteActive = true;
|
|
246
|
+
rest = rest.slice(startIndex + BRACKET_PASTE_START.length);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const endIndex = rest.indexOf(BRACKET_PASTE_END);
|
|
250
|
+
if (endIndex < 0) {
|
|
251
|
+
if (rest.length > 0)
|
|
252
|
+
bracketPasteHasRawPayload = true;
|
|
253
|
+
bracketPasteBuffer += rest;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (endIndex > 0)
|
|
257
|
+
bracketPasteHasRawPayload = true;
|
|
258
|
+
bracketPasteBuffer += rest.slice(0, endIndex);
|
|
259
|
+
bracketPasteActive = false;
|
|
260
|
+
applyPastedText(bracketPasteBuffer);
|
|
261
|
+
bracketPasteBuffer = '';
|
|
262
|
+
bracketPasteHasRawPayload = false;
|
|
263
|
+
suppressLineEventsUntil = Math.max(suppressLineEventsUntil, Date.now() + 60);
|
|
264
|
+
showPrompt();
|
|
265
|
+
rest = rest.slice(endIndex + BRACKET_PASTE_END.length);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
218
268
|
showPrompt();
|
|
219
269
|
// Handle paste from clipboard
|
|
220
270
|
const handlePaste = async () => {
|
|
@@ -255,6 +305,8 @@ export const createMultilineTextPrompt = () => {
|
|
|
255
305
|
rl.on('line', (line) => {
|
|
256
306
|
if (cancelled)
|
|
257
307
|
return;
|
|
308
|
+
if ((bracketPasteActive && bracketPasteHasRawPayload) || Date.now() < suppressLineEventsUntil)
|
|
309
|
+
return;
|
|
258
310
|
const now = Date.now();
|
|
259
311
|
if (pendingEnterSubmit) {
|
|
260
312
|
const likelyPasteFlow = now < recentPasteBurstUntil;
|
|
@@ -322,6 +374,7 @@ export const createMultilineTextPrompt = () => {
|
|
|
322
374
|
if (cancelled)
|
|
323
375
|
return;
|
|
324
376
|
const text = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk ?? '');
|
|
377
|
+
ingestBracketedPasteChunk(text);
|
|
325
378
|
updateBracketPasteState(text);
|
|
326
379
|
notePossiblePasteBurst(text);
|
|
327
380
|
};
|
|
@@ -362,17 +415,7 @@ export const createMultilineTextPrompt = () => {
|
|
|
362
415
|
if (pasted) {
|
|
363
416
|
// Clear current line and show pasted content
|
|
364
417
|
output.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
|
|
365
|
-
|
|
366
|
-
if (pastedLines.length > 1) {
|
|
367
|
-
// Multiline paste
|
|
368
|
-
lines.push(...pastedLines.slice(0, -1));
|
|
369
|
-
currentLine = pastedLines[pastedLines.length - 1];
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
372
|
-
// Single line paste
|
|
373
|
-
currentLine += pasted;
|
|
374
|
-
}
|
|
375
|
-
rl.line = currentLine;
|
|
418
|
+
applyPastedText(pasted);
|
|
376
419
|
output.write(`${pc.dim('> ')}${currentLine}`);
|
|
377
420
|
}
|
|
378
421
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@levu/snap",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"snap": "./dist/cli-entry.js"
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"dev": "tsx src/cli-entry.ts"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@clack/prompts": "^1.0.0"
|
|
15
|
+
"@clack/prompts": "^1.0.0",
|
|
16
|
+
"picocolors": "^1.1.1"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
19
|
"@types/node": "^22.10.2",
|