@code-yeongyu/senpi-tui 2026.11.5-1 → 2026.11.6-2
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/dist/keys.js +6 -4
- package/dist/stdin-buffer.d.ts +3 -1
- package/dist/stdin-buffer.js +37 -13
- package/package.json +1 -1
package/dist/keys.js
CHANGED
|
@@ -905,8 +905,10 @@ export function matchesKey(data, keyId) {
|
|
|
905
905
|
if (data === `\x1b${rawCtrl}`)
|
|
906
906
|
return true;
|
|
907
907
|
}
|
|
908
|
-
if (modifier === MODIFIERS.alt &&
|
|
909
|
-
// Legacy: alt+printable key is ESC followed by the key
|
|
908
|
+
if (modifier === MODIFIERS.alt && (isLetter || isDigit || SYMBOL_KEYS.has(key))) {
|
|
909
|
+
// Legacy: alt+printable key is ESC followed by the key. Accepted with Kitty active too: a
|
|
910
|
+
// conforming Kitty terminal sends Escape as CSI 27 u, and under ConPTY the reply comes from the
|
|
911
|
+
// outer terminal while the console host still encodes Alt as ESC <key> (#6058).
|
|
910
912
|
if (data === `\x1b${key}`)
|
|
911
913
|
return true;
|
|
912
914
|
}
|
|
@@ -1057,9 +1059,9 @@ export function parseKey(data) {
|
|
|
1057
1059
|
return "alt+left";
|
|
1058
1060
|
if (!_kittyProtocolActive && data === "\x1bF")
|
|
1059
1061
|
return "alt+right";
|
|
1060
|
-
if (
|
|
1062
|
+
if (data.length === 2 && data[0] === "\x1b") {
|
|
1061
1063
|
const code = data.charCodeAt(1);
|
|
1062
|
-
if (code >= 1 && code <= 26) {
|
|
1064
|
+
if (!_kittyProtocolActive && code >= 1 && code <= 26) {
|
|
1063
1065
|
return `ctrl+alt+${String.fromCharCode(code + 96)}`;
|
|
1064
1066
|
}
|
|
1065
1067
|
// Legacy alt+letter/digit/symbol (ESC followed by the key)
|
package/dist/stdin-buffer.d.ts
CHANGED
|
@@ -62,8 +62,10 @@ export declare class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
|
|
|
62
62
|
private readonly clock;
|
|
63
63
|
private lastInputAt;
|
|
64
64
|
private heldNewline;
|
|
65
|
-
private
|
|
65
|
+
private heldNewlineKind;
|
|
66
66
|
private lastBurstPasteAt;
|
|
67
|
+
/** True when the last burst paste's own line breaks were bare CR (an unbracketed paste from xterm/VTE/iTerm2). */
|
|
68
|
+
private lastBurstPasteUsesCrLineBreaks;
|
|
67
69
|
private burstTimer;
|
|
68
70
|
constructor(options?: StdinBufferOptions);
|
|
69
71
|
process(data: string | Buffer): void;
|
package/dist/stdin-buffer.js
CHANGED
|
@@ -246,7 +246,9 @@ export class StdinBuffer extends EventEmitter {
|
|
|
246
246
|
this.pasteBuffer = "";
|
|
247
247
|
this.decoder = new StringDecoder("utf8");
|
|
248
248
|
this.heldNewline = "";
|
|
249
|
-
this.
|
|
249
|
+
this.heldNewlineKind = "keystroke";
|
|
250
|
+
/** True when the last burst paste's own line breaks were bare CR (an unbracketed paste from xterm/VTE/iTerm2). */
|
|
251
|
+
this.lastBurstPasteUsesCrLineBreaks = false;
|
|
250
252
|
this.burstTimer = null;
|
|
251
253
|
this.timeoutMs = options.timeout ?? DEFAULT_SEQUENCE_TIMEOUT_MS;
|
|
252
254
|
this.escapeTimeoutMs = options.escapeTimeout ?? DEFAULT_ESCAPE_TIMEOUT_MS;
|
|
@@ -316,10 +318,15 @@ export class StdinBuffer extends EventEmitter {
|
|
|
316
318
|
if (burstGap !== undefined && burstGap >= this.burstWindowMs) {
|
|
317
319
|
this.releaseHeldNewline();
|
|
318
320
|
}
|
|
321
|
+
else if (this.heldNewlineKind === "enter" && !this.buffer.startsWith("\n")) {
|
|
322
|
+
// A held Enter only continues into the LF half of a CRLF pair; any other read means the submit
|
|
323
|
+
// stands, so the Enter lands before that read is framed.
|
|
324
|
+
this.releaseHeldNewline();
|
|
325
|
+
}
|
|
319
326
|
else {
|
|
320
327
|
this.buffer = this.heldNewline + this.buffer;
|
|
321
328
|
this.heldNewline = "";
|
|
322
|
-
this.
|
|
329
|
+
this.heldNewlineKind = "keystroke";
|
|
323
330
|
}
|
|
324
331
|
}
|
|
325
332
|
if (this.pasteMode) {
|
|
@@ -404,15 +411,28 @@ export class StdinBuffer extends EventEmitter {
|
|
|
404
411
|
if (breaks === 0) {
|
|
405
412
|
return false;
|
|
406
413
|
}
|
|
414
|
+
const trailing = text.match(TRAILING_LINE_BREAK_RUN)?.[0];
|
|
415
|
+
const trailingEndsWithCr = trailing !== undefined && trailing.endsWith("\r");
|
|
416
|
+
// An unbracketed paste from a terminal that turns pasted newlines into bare CR (xterm, VTE, iTerm2) carries
|
|
417
|
+
// no LF at all, so its own trailing CR closes that paste. Only a burst whose breaks include LF or CRLF can
|
|
418
|
+
// end in the Enter key that submits it; anything else would submit a human's CR-only paste.
|
|
419
|
+
const crOnlyPasteBurst = trailingEndsWithCr && !text.includes("\n") && /[^\r\n]/.test(text);
|
|
407
420
|
// Typing delivers one key per read, so a single read that holds two line breaks, or text after a
|
|
408
421
|
// line break, is pasted. Both carry text: a run of bare Enters stays keystrokes.
|
|
409
422
|
if ((breaks >= 2 || LINE_BREAK_BEFORE_TEXT.test(text)) && /[^\r\n]/.test(text)) {
|
|
423
|
+
const endsWithEnter = trailingEndsWithCr && !crOnlyPasteBurst;
|
|
424
|
+
const body = endsWithEnter ? text.slice(0, -1) : text;
|
|
410
425
|
this.buffer = "";
|
|
411
426
|
this.lastBurstPasteAt = this.lastInputAt;
|
|
412
|
-
this.
|
|
427
|
+
this.lastBurstPasteUsesCrLineBreaks = !body.includes("\n");
|
|
428
|
+
this.emit("paste", body);
|
|
429
|
+
if (endsWithEnter) {
|
|
430
|
+
// The paste is committed first, so the Enter that trails it submits the block after it. It waits out
|
|
431
|
+
// the window only because a CRLF split across reads still belongs to its paste.
|
|
432
|
+
this.holdTrailingNewline("\r", "enter");
|
|
433
|
+
}
|
|
413
434
|
return true;
|
|
414
435
|
}
|
|
415
|
-
const trailing = text.match(TRAILING_LINE_BREAK_RUN)?.[0];
|
|
416
436
|
if (trailing === undefined || burstGap === undefined || burstGap >= this.burstWindowMs) {
|
|
417
437
|
return false;
|
|
418
438
|
}
|
|
@@ -422,20 +442,23 @@ export class StdinBuffer extends EventEmitter {
|
|
|
422
442
|
const endsPaste = this.lastBurstPasteAt !== undefined &&
|
|
423
443
|
this.lastInputAt !== undefined &&
|
|
424
444
|
this.lastInputAt - this.lastBurstPasteAt < this.burstWindowMs;
|
|
425
|
-
// A
|
|
426
|
-
|
|
445
|
+
// A CR closing a burst whose own breaks were bare CR continues that paste, exactly as the same text in one
|
|
446
|
+
// read keeps it; after an LF/CRLF paste it is the Enter key the sender pressed.
|
|
447
|
+
const crContinuesPaste = endsPaste && this.lastBurstPasteUsesCrLineBreaks;
|
|
448
|
+
// A read of bare line breaks is a keystroke unless it closes a paste, and a submit is never held back.
|
|
449
|
+
if ((trailingEndsWithCr && !crContinuesPaste) || (head.length === 0 && !endsPaste)) {
|
|
427
450
|
return false;
|
|
428
451
|
}
|
|
429
452
|
for (const chunk of head) {
|
|
430
453
|
this.emitDataSequence(chunk);
|
|
431
454
|
}
|
|
432
455
|
this.buffer = "";
|
|
433
|
-
this.holdTrailingNewline(trailing, endsPaste);
|
|
456
|
+
this.holdTrailingNewline(trailing, endsPaste ? "paste" : "keystroke");
|
|
434
457
|
return true;
|
|
435
458
|
}
|
|
436
|
-
holdTrailingNewline(run,
|
|
459
|
+
holdTrailingNewline(run, kind) {
|
|
437
460
|
this.heldNewline = run;
|
|
438
|
-
this.
|
|
461
|
+
this.heldNewlineKind = kind;
|
|
439
462
|
if (this.burstTimer) {
|
|
440
463
|
clearTimeout(this.burstTimer);
|
|
441
464
|
}
|
|
@@ -449,10 +472,10 @@ export class StdinBuffer extends EventEmitter {
|
|
|
449
472
|
return;
|
|
450
473
|
}
|
|
451
474
|
const held = this.heldNewline;
|
|
452
|
-
const
|
|
475
|
+
const kind = this.heldNewlineKind;
|
|
453
476
|
this.heldNewline = "";
|
|
454
|
-
this.
|
|
455
|
-
if (
|
|
477
|
+
this.heldNewlineKind = "keystroke";
|
|
478
|
+
if (kind === "paste") {
|
|
456
479
|
this.emit("paste", held);
|
|
457
480
|
return;
|
|
458
481
|
}
|
|
@@ -509,8 +532,9 @@ export class StdinBuffer extends EventEmitter {
|
|
|
509
532
|
this.burstTimer = null;
|
|
510
533
|
}
|
|
511
534
|
this.heldNewline = "";
|
|
512
|
-
this.
|
|
535
|
+
this.heldNewlineKind = "keystroke";
|
|
513
536
|
this.lastBurstPasteAt = undefined;
|
|
537
|
+
this.lastBurstPasteUsesCrLineBreaks = false;
|
|
514
538
|
this.buffer = "";
|
|
515
539
|
this.pasteMode = false;
|
|
516
540
|
this.pasteBuffer = "";
|
package/package.json
CHANGED