@oxecli/oxe 1.0.67 → 1.0.69

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.
Files changed (2) hide show
  1. package/dist/ui.js +52 -26
  2. package/package.json +1 -1
package/dist/ui.js CHANGED
@@ -9,7 +9,7 @@ export function enableRawStdin() {
9
9
  readline.emitKeypressEvents(process.stdin);
10
10
  if (process.stdin.isTTY) {
11
11
  process.stdin.setRawMode(true);
12
- process.stdout.write("\x1b[?9001h\x1b[>1u");
12
+ process.stdout.write("\x1b[>1u");
13
13
  }
14
14
  process.stdin.resume();
15
15
  hideCursor();
@@ -927,7 +927,7 @@ export function renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor
927
927
  * application (SS3): ESC O M
928
928
  * win32-input-mode: ESC [ G ; R ; C ; VK ; MOD u
929
929
  * (VK = 13 for Enter, MOD = raw bitmask where
930
- * bit0 = Shift)
930
+ * bit2 = Shift)
931
931
  */
932
932
  function parseEnterSequence(seq) {
933
933
  if (!seq)
@@ -939,7 +939,7 @@ function parseEnterSequence(seq) {
939
939
  if (vk !== 13)
940
940
  return null;
941
941
  const mod = parseInt(m[5], 10);
942
- return mod & 1 ? "newline" : "submit";
942
+ return mod & 4 ? "newline" : "submit";
943
943
  }
944
944
  // modifyOtherKeys / kitty: ESC [ 27 ; M ; 13 ~ (or _)
945
945
  m = seq.match(/^\x1b\[27;(\d+);13[~_]$/);
@@ -1045,9 +1045,10 @@ function decodeWin32Key(seq) {
1045
1045
  return null;
1046
1046
  const vk = parseInt(m[4], 10);
1047
1047
  const mod = parseInt(m[5], 10);
1048
- const shift = (mod & 1) === 1;
1049
- const ctrl = (mod & 4) === 4;
1050
- const alt = (mod & 2) === 2;
1048
+ // win32-input-mode MOD bitmask: bit2 = Shift, bit1 = Ctrl, bit0 = Alt
1049
+ const shift = (mod & 4) === 4;
1050
+ const ctrl = (mod & 2) === 2;
1051
+ const alt = (mod & 1) === 1;
1051
1052
  const cmd = (c) => ({ type: "cmd", cmd: c });
1052
1053
  const text = (t) => ({ type: "text", text: t });
1053
1054
  // Control combos
@@ -1136,14 +1137,19 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
1136
1137
  // `\x1b[27;2;13~`), which Node's readline splits into several keypress
1137
1138
  // events instead of one, so they must be reassembled here.
1138
1139
  let modSeq = "";
1140
+ let modSeqTimeout = null;
1139
1141
  readline.emitKeypressEvents(process.stdin);
1140
1142
  if (process.stdin.isTTY) {
1141
1143
  process.stdin.setRawMode(true);
1142
1144
  process.stdout.write("\x1b[?2004h");
1143
- // Enable win32-input-mode (preferred by Windows Terminal) and CSI-u
1144
- // disambiguation so Shift+Enter arrives as a distinct code instead of a
1145
- // plain Enter. Harmless if a terminal doesn't support them.
1146
- process.stdout.write("\x1b[?9001h\x1b[>1u");
1145
+ // Enable CSI-u disambiguation so Shift+Enter arrives as a distinct code
1146
+ // (`\x1b[13;2u`) instead of a plain Enter. Plain keys still arrive as
1147
+ // single keypress events. Deliberately NOT enabling win32-input-mode
1148
+ // (`\x1b[?9001h`): it rewrites EVERY keystroke into a long CSI sequence
1149
+ // that readline splits across many events, and an interrupted split
1150
+ // leaves modSeq stuck so the next keystroke is silently dropped —
1151
+ // that regression made typing in the prompt impossible.
1152
+ process.stdout.write("\x1b[>1u");
1147
1153
  }
1148
1154
  process.stdin.resume();
1149
1155
  hideCursor();
@@ -1166,10 +1172,12 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
1166
1172
  if (done)
1167
1173
  return;
1168
1174
  done = true;
1169
- process.stdout.write("\x1b[?2004l\x1b[?9001l");
1175
+ process.stdout.write("\x1b[?2004l");
1170
1176
  process.stdin.setRawMode(false);
1171
1177
  if (pasteBurstTimer)
1172
1178
  clearTimeout(pasteBurstTimer);
1179
+ if (modSeqTimeout)
1180
+ clearTimeout(modSeqTimeout);
1173
1181
  clearBox();
1174
1182
  process.stdin.removeListener("keypress", onKeypress);
1175
1183
  process.stdin.pause();
@@ -1583,25 +1591,43 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
1583
1591
  }
1584
1592
  };
1585
1593
  const onKeypress = (str, key) => {
1586
- // Reassemble modifier-prefixed / win32-input-mode CSI sequences, which
1587
- // Node's readline splits into several keypress events (e.g. Shift+Enter
1588
- // arriving as `\x1b[27;2;` + "1" + "3" + "~", or a win32-input-mode
1589
- // sequence `\x1b[0;1;1;13;2u` arriving piecemeal). A "partial CSI" is
1590
- // any ESC [ sequence readline left unterminated; we keep appending until
1591
- // it ends in a terminator, then classify it.
1594
+ // Reassemble modifier-prefixed CSI sequences that Node's readline may
1595
+ // split across keypress events (e.g. `\x1b[13;2u` arriving as `\x1b[13;2`
1596
+ // + "u"). Only start/continue accumulation on bytes that can belong to a
1597
+ // CSI sequence: digits / `;` while incomplete, or a terminator byte
1598
+ // [A-Z~_u] that completes it. Any other keystroke (a typed letter, for
1599
+ // instance) ABANDONS the partial instead of being swallowed, so a stuck
1600
+ // partial can never eat input.
1592
1601
  const seq = key?.sequence || str || "";
1593
- if (modSeq) {
1594
- modSeq += str || seq || "";
1595
- if (/^\x1b\[[\d;]*[a-zA-Z~_u]$/.test(modSeq)) {
1596
- const full = modSeq;
1602
+ if (seq.startsWith("\x1b[") && /^\x1b\[[\d;]*$/.test(seq)) {
1603
+ modSeq = seq;
1604
+ if (modSeqTimeout)
1605
+ clearTimeout(modSeqTimeout);
1606
+ modSeqTimeout = setTimeout(() => {
1597
1607
  modSeq = "";
1598
- handleRawSequence(full);
1599
- }
1608
+ modSeqTimeout = null;
1609
+ }, 200);
1600
1610
  return;
1601
1611
  }
1602
- if (/^\x1b\[[\d;]*$/.test(seq)) {
1603
- modSeq = seq;
1604
- return;
1612
+ if (modSeq) {
1613
+ const s = str ?? seq ?? "";
1614
+ const completes = /^\x1b\[[\d;]*[A-Z~_u]$/.test(modSeq + s);
1615
+ if (/^[0-9;]$/.test(s) || completes) {
1616
+ modSeq += s;
1617
+ if (completes) {
1618
+ const full = modSeq;
1619
+ modSeq = "";
1620
+ if (modSeqTimeout)
1621
+ clearTimeout(modSeqTimeout);
1622
+ modSeqTimeout = null;
1623
+ handleRawSequence(full);
1624
+ }
1625
+ return;
1626
+ }
1627
+ modSeq = "";
1628
+ if (modSeqTimeout)
1629
+ clearTimeout(modSeqTimeout);
1630
+ modSeqTimeout = null;
1605
1631
  }
1606
1632
  if (consumeBracketedPaste(str, key))
1607
1633
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.67",
3
+ "version": "1.0.69",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },