@oxecli/oxe 1.0.104 → 1.0.105

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 +24 -55
  2. package/package.json +1 -1
package/dist/ui.js CHANGED
@@ -1394,7 +1394,6 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1394
1394
  let draft = null;
1395
1395
  let bracketedPaste = false;
1396
1396
  let bracketedPasteBuffer = "";
1397
- let bracketedPasteSince = 0;
1398
1397
  let pasteBurst = false;
1399
1398
  let pasteBurstTimer = null;
1400
1399
  readline.emitKeypressEvents(process.stdin);
@@ -1719,19 +1718,17 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1719
1718
  const end = "\x1b[201~";
1720
1719
  const sequence = key?.sequence || "";
1721
1720
  const value = str || "";
1722
- if (!bracketedPaste && (sequence === start || value === start)) {
1721
+ // A new paste-start while already inside a paste means the previous paste
1722
+ // was aborted (its end marker never arrived). Reset and start over so we
1723
+ // never swallow subsequent typing.
1724
+ if ((key?.name === "paste-start" || sequence === start || value === start)) {
1723
1725
  bracketedPaste = true;
1724
- bracketedPasteSince = Date.now();
1725
1726
  bracketedPasteBuffer = "";
1726
1727
  const inline = value === start ? "" : value.startsWith(start) ? value.slice(start.length) : "";
1727
1728
  if (inline)
1728
1729
  bracketedPasteBuffer = inline;
1729
1730
  if (inline.includes(end)) {
1730
- const endAt = inline.indexOf(end);
1731
- const pasted = inline
1732
- .slice(0, endAt)
1733
- .replace(/\r\n/g, "\n")
1734
- .replace(/\r/g, "\n");
1731
+ const pasted = inline.slice(0, inline.indexOf(end)).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1735
1732
  bracketedPaste = false;
1736
1733
  bracketedPasteBuffer = "";
1737
1734
  if (pasted)
@@ -1742,30 +1739,12 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1742
1739
  }
1743
1740
  if (!bracketedPaste)
1744
1741
  return false;
1745
- // Safety net: if the paste-end marker (\x1b[201~) was lost interrupted
1746
- // paste, split escape sequence, or a terminal quirk bracketedPaste stays
1747
- // true and every later keystroke is swallowed here, which looks like the
1748
- // input field locked up (even Ctrl+C is captured as a paste chunk). If the
1749
- // paste has been open far longer than any real paste takes and a fresh
1750
- // single-printable-char keystroke arrives, flush the buffered text as
1751
- // literal input and reset so typing works again.
1752
- if (Date.now() - bracketedPasteSince > 3000 &&
1753
- str &&
1754
- str.length === 1 &&
1755
- !key?.ctrl &&
1756
- !key?.meta) {
1757
- const pasted = bracketedPasteBuffer
1758
- .replace(/\r\n/g, "\n")
1759
- .replace(/\r/g, "\n");
1760
- bracketedPaste = false;
1761
- bracketedPasteBuffer = "";
1762
- if (pasted) {
1763
- insert(pasted, true);
1764
- repaint();
1765
- }
1766
- return false; // let this keystroke be handled normally below
1767
- }
1768
- if (sequence === end || value === end) {
1742
+ // Close on the end marker. readline emits it as a dedicated keypress
1743
+ // (key.name === "paste-end", sequence === \x1b[201~); also scan the raw
1744
+ // value in case a terminal delivers it embedded in a larger chunk.
1745
+ const endAt = value.indexOf(end);
1746
+ if (key?.name === "paste-end" || sequence === end || value === end || endAt >= 0) {
1747
+ bracketedPasteBuffer += endAt >= 0 ? value.slice(0, endAt) : "";
1769
1748
  const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1770
1749
  bracketedPaste = false;
1771
1750
  bracketedPasteBuffer = "";
@@ -1775,21 +1754,8 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1775
1754
  return true;
1776
1755
  }
1777
1756
  const chunk = value || (sequence && !sequence.startsWith("\x1b[") ? sequence : "");
1778
- if (chunk) {
1779
- const endAt = chunk.indexOf(end);
1780
- if (endAt >= 0) {
1781
- bracketedPasteBuffer += chunk.slice(0, endAt);
1782
- const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1783
- bracketedPaste = false;
1784
- bracketedPasteBuffer = "";
1785
- if (pasted)
1786
- insert(pasted, true);
1787
- repaint();
1788
- }
1789
- else {
1790
- bracketedPasteBuffer += chunk;
1791
- }
1792
- }
1757
+ if (chunk)
1758
+ bracketedPasteBuffer += chunk;
1793
1759
  return true;
1794
1760
  };
1795
1761
  const markPasteBurst = () => {
@@ -1802,14 +1768,9 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1802
1768
  }, 500);
1803
1769
  };
1804
1770
  const onKeypress = (str, key) => {
1805
- if (consumeBracketedPaste(str, key))
1806
- return;
1807
- if (pasteBurst && str && str.length === 1 && !key?.ctrl && !key?.meta) {
1808
- pasteBurst = false;
1809
- if (pasteBurstTimer)
1810
- clearTimeout(pasteBurstTimer);
1811
- pasteBurstTimer = null;
1812
- }
1771
+ // Interrupts (Ctrl+C/D/Z) must be handled BEFORE bracketed-paste capture so
1772
+ // that even a stuck/interrupted paste can always be escaped — otherwise the
1773
+ // paste handler swallows them and the field appears locked with no way out.
1813
1774
  if (key && key.ctrl && key.name === "c") {
1814
1775
  settle(new Error("interrupt"), true);
1815
1776
  return;
@@ -1818,6 +1779,14 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1818
1779
  settle(new Error("eof"), true);
1819
1780
  return;
1820
1781
  }
1782
+ if (consumeBracketedPaste(str, key))
1783
+ return;
1784
+ if (pasteBurst && str && str.length === 1 && !key?.ctrl && !key?.meta) {
1785
+ pasteBurst = false;
1786
+ if (pasteBurstTimer)
1787
+ clearTimeout(pasteBurstTimer);
1788
+ pasteBurstTimer = null;
1789
+ }
1821
1790
  if (key && key.ctrl && key.name === "a") {
1822
1791
  cursor = 0;
1823
1792
  repaint();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.104",
3
+ "version": "1.0.105",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },