@oh-my-pi/pi-tui 16.2.6 → 16.2.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
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.2.7] - 2026-06-30
6
+
7
+ ### Fixed
8
+
9
+ - Fixed an issue where a fast double-Escape keypress was swallowed and ignored, preventing double-escape gestures and subsequent Escape key handlers from firing.
10
+
5
11
  ## [16.2.3] - 2026-06-28
6
12
 
7
13
  ### Added
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-tui",
4
- "version": "16.2.6",
4
+ "version": "16.2.8",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -37,8 +37,8 @@
37
37
  "fmt": "biome format --write ."
38
38
  },
39
39
  "dependencies": {
40
- "@oh-my-pi/pi-natives": "16.2.6",
41
- "@oh-my-pi/pi-utils": "16.2.6",
40
+ "@oh-my-pi/pi-natives": "16.2.8",
41
+ "@oh-my-pi/pi-utils": "16.2.8",
42
42
  "lru-cache": "11.5.1",
43
43
  "marked": "^18.0.5"
44
44
  },
@@ -241,13 +241,19 @@ function extractCompleteSequences(buffer: string): { sequences: string[]; remain
241
241
  end++;
242
242
  continue;
243
243
  }
244
- // "\x1b\x1b" alone parses as "complete" (legacy alt+esc), but when the
245
- // next byte opens a CSI/SS3 ("[" or "O") this is really ESC prefixing
246
- // another sequence (meta-CSI, or a held Esc keypress joined by a
247
- // follower). Consuming two bytes here would tear the follower and leak
248
- // its tail as typed text (settings search filling with "[B" or
249
- // "[<35;22;17M"). Keep growing; when the buffer ends here, hold the
250
- // partial for the flush window so the disambiguating byte can arrive.
244
+ // "\x1b\x1b" is one of three things:
245
+ // 1. ESC prefixing CSI/SS3 (meta-CSI, held Esc joined by a follower):
246
+ // next byte is "[" or "O" keep growing so the full sequence stays
247
+ // together. Consuming two bytes here would tear the follower and
248
+ // leak its tail as typed text (settings search filling with "[B"
249
+ // or "[<35;22;17M").
250
+ // 2. ESC followed by a legacy Alt chord (`\x1bd`, `\x1b\x7f`, …):
251
+ // emit the first ESC, then restart at the second ESC so downstream
252
+ // parsing still sees the Alt chord as one keypress (#3860 review).
253
+ // 3. Two real Esc keypresses bursted by terminal input batching:
254
+ // when the buffer ends here, hold the partial for the flush window
255
+ // so case 1/2 can still arrive; if no follower arrives, `flush()`
256
+ // splits the held remainder into two ESC events (#3857).
251
257
  if (candidate === `${ESC}${ESC}`) {
252
258
  if (end >= length) {
253
259
  return { sequences, remainder: buffer.slice(pos) };
@@ -257,6 +263,10 @@ function extractCompleteSequences(buffer: string): { sequences: string[]; remain
257
263
  end++;
258
264
  continue;
259
265
  }
266
+ sequences.push(ESC);
267
+ pos += 1;
268
+ consumed = true;
269
+ break;
260
270
  }
261
271
  // ESC + SGR mouse report is never a meta chord: alt-modified mouse
262
272
  // reports carry the modifier in the button bits, not an ESC prefix.
@@ -605,10 +615,18 @@ export class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
605
615
  return [];
606
616
  }
607
617
 
608
- const sequences = [this.#buffer];
618
+ const buffered = this.#buffer;
609
619
  this.#buffer = "";
610
620
  this.#pendingKittyPrintableCodepoint = undefined;
611
- return sequences;
621
+ // Bare double-ESC remainder (no disambiguating "[" / "O" arrived in time):
622
+ // two real Esc keypresses bursted by terminal batching, not a meta-CSI/SS3
623
+ // prefix. `parseKey` returns undefined for the combined chunk, so a single
624
+ // emission swallows the double-escape gesture (#3857). Mirror the inline
625
+ // split in `extractCompleteSequences` and deliver two ESC events.
626
+ if (buffered === `${ESC}${ESC}`) {
627
+ return [ESC, ESC];
628
+ }
629
+ return [buffered];
612
630
  }
613
631
 
614
632
  clear(): void {