@oxecli/oxe 1.0.112 → 1.0.113

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 +49 -1
  2. package/package.json +1 -1
package/dist/ui.js CHANGED
@@ -1249,6 +1249,17 @@ export const PROMPT_PLACEHOLDER = "Describe a coding task, or type /help for com
1249
1249
  const MAX_PROMPT_DISPLAY_LINES = 12;
1250
1250
  const MAX_PROMPT_PASTE_CHARS = 400;
1251
1251
  const MAX_PROMPT_CHARS = 500;
1252
+ /**
1253
+ * Watchdog for bracketed paste. Some terminals (notably Windows Terminal /
1254
+ * conpty) occasionally drop the paste-END marker, which would leave the field
1255
+ * permanently capturing keystrokes (a "locked" prompt). If a paste-start
1256
+ * arrives but no paste-end AND no further paste data shows up within this
1257
+ * window, we treat the paste as aborted, release capture, and keep whatever
1258
+ * text already arrived so input is never lost. The window resets on every
1259
+ * received paste chunk, so a genuine (continuously-streaming) paste is never
1260
+ * cut short.
1261
+ */
1262
+ const PASTE_GUARD_MS = 3000;
1252
1263
  function shouldCollapsePaste(text) {
1253
1264
  return (text.split("\n").length > MAX_PROMPT_DISPLAY_LINES ||
1254
1265
  text.length > MAX_PROMPT_PASTE_CHARS);
@@ -1555,6 +1566,7 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1555
1566
  let draft = null;
1556
1567
  let bracketedPaste = false;
1557
1568
  let bracketedPasteBuffer = "";
1569
+ let pasteGuardTimer = null;
1558
1570
  let pasteBurst = false;
1559
1571
  let pasteBurstTimer = null;
1560
1572
  readline.emitKeypressEvents(process.stdin);
@@ -1593,6 +1605,10 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1593
1605
  bracketedPasteBuffer = "";
1594
1606
  if (pasteBurstTimer)
1595
1607
  clearTimeout(pasteBurstTimer);
1608
+ if (pasteGuardTimer) {
1609
+ clearTimeout(pasteGuardTimer);
1610
+ pasteGuardTimer = null;
1611
+ }
1596
1612
  if (isErr) {
1597
1613
  clearBox();
1598
1614
  dockSetInactive();
@@ -1898,17 +1914,46 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1898
1914
  const end = "\x1b[201~";
1899
1915
  const sequence = key?.sequence || "";
1900
1916
  const value = str || "";
1917
+ // A paste-end marker must close the capture. If it never arrives and no
1918
+ // more paste data follows, the guard below auto-releases the field so it
1919
+ // can't lock. (definitions hoisted below)
1920
+ const armPasteGuard = () => {
1921
+ if (pasteGuardTimer)
1922
+ clearTimeout(pasteGuardTimer);
1923
+ pasteGuardTimer = setTimeout(() => {
1924
+ pasteGuardTimer = null;
1925
+ if (!bracketedPaste)
1926
+ return;
1927
+ // The terminal dropped the paste-end (a Windows/conpty failure). Bail
1928
+ // out of capture mode and insert whatever already arrived so nothing
1929
+ // typed afterward is swallowed.
1930
+ bracketedPaste = false;
1931
+ const leftover = bracketedPasteBuffer;
1932
+ bracketedPasteBuffer = "";
1933
+ if (leftover)
1934
+ insert(leftover, true);
1935
+ repaint();
1936
+ }, PASTE_GUARD_MS);
1937
+ };
1938
+ const disarmPasteGuard = () => {
1939
+ if (pasteGuardTimer) {
1940
+ clearTimeout(pasteGuardTimer);
1941
+ pasteGuardTimer = null;
1942
+ }
1943
+ };
1901
1944
  // A new paste-start while already inside a paste means the previous paste
1902
1945
  // was aborted (its end marker never arrived). Reset and start over so we
1903
1946
  // never swallow subsequent typing.
1904
1947
  if ((key?.name === "paste-start" || sequence === start || value === start)) {
1905
1948
  bracketedPaste = true;
1906
1949
  bracketedPasteBuffer = "";
1950
+ armPasteGuard();
1907
1951
  const inline = value === start ? "" : value.startsWith(start) ? value.slice(start.length) : "";
1908
1952
  if (inline)
1909
1953
  bracketedPasteBuffer = inline;
1910
1954
  if (inline.includes(end)) {
1911
1955
  const pasted = inline.slice(0, inline.indexOf(end)).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1956
+ disarmPasteGuard();
1912
1957
  bracketedPaste = false;
1913
1958
  bracketedPasteBuffer = "";
1914
1959
  if (pasted)
@@ -1926,6 +1971,7 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1926
1971
  if (key?.name === "paste-end" || sequence === end || value === end || endAt >= 0) {
1927
1972
  bracketedPasteBuffer += endAt >= 0 ? value.slice(0, endAt) : "";
1928
1973
  const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1974
+ disarmPasteGuard();
1929
1975
  bracketedPaste = false;
1930
1976
  bracketedPasteBuffer = "";
1931
1977
  if (pasted)
@@ -1934,8 +1980,10 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1934
1980
  return true;
1935
1981
  }
1936
1982
  const chunk = value || (sequence && !sequence.startsWith("\x1b[") ? sequence : "");
1937
- if (chunk)
1983
+ if (chunk) {
1938
1984
  bracketedPasteBuffer += chunk;
1985
+ armPasteGuard();
1986
+ }
1939
1987
  return true;
1940
1988
  };
1941
1989
  const markPasteBurst = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.112",
3
+ "version": "1.0.113",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },