@oxecli/oxe 1.0.112 → 1.0.114
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/ui.js +68 -9
- 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);
|
|
@@ -1492,18 +1503,29 @@ export function renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor
|
|
|
1492
1503
|
let totalRows = body.length + 2;
|
|
1493
1504
|
if (hints && (hints.left || hints.right)) {
|
|
1494
1505
|
let left = hints.left ?? "";
|
|
1495
|
-
|
|
1506
|
+
let right = hints.right ?? "";
|
|
1496
1507
|
// Inset the hint row inside the box (box spans columns 0..boxW-1): a
|
|
1497
1508
|
// leading space gives left-edge padding, and the right hint ends one
|
|
1498
|
-
// column before the box's right border for the same padding. Truncate
|
|
1499
|
-
//
|
|
1500
|
-
//
|
|
1509
|
+
// column before the box's right border for the same padding. Truncate so
|
|
1510
|
+
// the row NEVER exceeds the box width on narrow/scaled terminals: the left
|
|
1511
|
+
// hint is cut first to keep the (priority) right hint intact, then the
|
|
1512
|
+
// right hint is cut as a last resort. Otherwise a long right hint would
|
|
1513
|
+
// overflow past the border instead of scaling down.
|
|
1501
1514
|
const hintMax = boxW - 2;
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1515
|
+
const maxText = Math.max(0, hintMax - 1); // reserve a >=1 gap between hints
|
|
1516
|
+
let leftW = plainLen(left);
|
|
1517
|
+
let rightW = plainLen(right);
|
|
1518
|
+
if (leftW + rightW > maxText) {
|
|
1519
|
+
const roomL = Math.max(0, maxText - rightW);
|
|
1520
|
+
left = truncateStyled(left, roomL);
|
|
1521
|
+
leftW = plainLen(left);
|
|
1522
|
+
}
|
|
1523
|
+
const roomR = Math.max(0, maxText - leftW);
|
|
1524
|
+
if (rightW > roomR) {
|
|
1525
|
+
right = truncateStyled(right, roomR);
|
|
1526
|
+
rightW = plainLen(right);
|
|
1505
1527
|
}
|
|
1506
|
-
const gap = Math.max(1,
|
|
1528
|
+
const gap = Math.max(1, hintMax - leftW - rightW);
|
|
1507
1529
|
lines.push(" " + left + " ".repeat(gap) + right);
|
|
1508
1530
|
totalRows += 1;
|
|
1509
1531
|
}
|
|
@@ -1555,6 +1577,7 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1555
1577
|
let draft = null;
|
|
1556
1578
|
let bracketedPaste = false;
|
|
1557
1579
|
let bracketedPasteBuffer = "";
|
|
1580
|
+
let pasteGuardTimer = null;
|
|
1558
1581
|
let pasteBurst = false;
|
|
1559
1582
|
let pasteBurstTimer = null;
|
|
1560
1583
|
readline.emitKeypressEvents(process.stdin);
|
|
@@ -1593,6 +1616,10 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1593
1616
|
bracketedPasteBuffer = "";
|
|
1594
1617
|
if (pasteBurstTimer)
|
|
1595
1618
|
clearTimeout(pasteBurstTimer);
|
|
1619
|
+
if (pasteGuardTimer) {
|
|
1620
|
+
clearTimeout(pasteGuardTimer);
|
|
1621
|
+
pasteGuardTimer = null;
|
|
1622
|
+
}
|
|
1596
1623
|
if (isErr) {
|
|
1597
1624
|
clearBox();
|
|
1598
1625
|
dockSetInactive();
|
|
@@ -1898,17 +1925,46 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1898
1925
|
const end = "\x1b[201~";
|
|
1899
1926
|
const sequence = key?.sequence || "";
|
|
1900
1927
|
const value = str || "";
|
|
1928
|
+
// A paste-end marker must close the capture. If it never arrives and no
|
|
1929
|
+
// more paste data follows, the guard below auto-releases the field so it
|
|
1930
|
+
// can't lock. (definitions hoisted below)
|
|
1931
|
+
const armPasteGuard = () => {
|
|
1932
|
+
if (pasteGuardTimer)
|
|
1933
|
+
clearTimeout(pasteGuardTimer);
|
|
1934
|
+
pasteGuardTimer = setTimeout(() => {
|
|
1935
|
+
pasteGuardTimer = null;
|
|
1936
|
+
if (!bracketedPaste)
|
|
1937
|
+
return;
|
|
1938
|
+
// The terminal dropped the paste-end (a Windows/conpty failure). Bail
|
|
1939
|
+
// out of capture mode and insert whatever already arrived so nothing
|
|
1940
|
+
// typed afterward is swallowed.
|
|
1941
|
+
bracketedPaste = false;
|
|
1942
|
+
const leftover = bracketedPasteBuffer;
|
|
1943
|
+
bracketedPasteBuffer = "";
|
|
1944
|
+
if (leftover)
|
|
1945
|
+
insert(leftover, true);
|
|
1946
|
+
repaint();
|
|
1947
|
+
}, PASTE_GUARD_MS);
|
|
1948
|
+
};
|
|
1949
|
+
const disarmPasteGuard = () => {
|
|
1950
|
+
if (pasteGuardTimer) {
|
|
1951
|
+
clearTimeout(pasteGuardTimer);
|
|
1952
|
+
pasteGuardTimer = null;
|
|
1953
|
+
}
|
|
1954
|
+
};
|
|
1901
1955
|
// A new paste-start while already inside a paste means the previous paste
|
|
1902
1956
|
// was aborted (its end marker never arrived). Reset and start over so we
|
|
1903
1957
|
// never swallow subsequent typing.
|
|
1904
1958
|
if ((key?.name === "paste-start" || sequence === start || value === start)) {
|
|
1905
1959
|
bracketedPaste = true;
|
|
1906
1960
|
bracketedPasteBuffer = "";
|
|
1961
|
+
armPasteGuard();
|
|
1907
1962
|
const inline = value === start ? "" : value.startsWith(start) ? value.slice(start.length) : "";
|
|
1908
1963
|
if (inline)
|
|
1909
1964
|
bracketedPasteBuffer = inline;
|
|
1910
1965
|
if (inline.includes(end)) {
|
|
1911
1966
|
const pasted = inline.slice(0, inline.indexOf(end)).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1967
|
+
disarmPasteGuard();
|
|
1912
1968
|
bracketedPaste = false;
|
|
1913
1969
|
bracketedPasteBuffer = "";
|
|
1914
1970
|
if (pasted)
|
|
@@ -1926,6 +1982,7 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1926
1982
|
if (key?.name === "paste-end" || sequence === end || value === end || endAt >= 0) {
|
|
1927
1983
|
bracketedPasteBuffer += endAt >= 0 ? value.slice(0, endAt) : "";
|
|
1928
1984
|
const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1985
|
+
disarmPasteGuard();
|
|
1929
1986
|
bracketedPaste = false;
|
|
1930
1987
|
bracketedPasteBuffer = "";
|
|
1931
1988
|
if (pasted)
|
|
@@ -1934,8 +1991,10 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
|
|
|
1934
1991
|
return true;
|
|
1935
1992
|
}
|
|
1936
1993
|
const chunk = value || (sequence && !sequence.startsWith("\x1b[") ? sequence : "");
|
|
1937
|
-
if (chunk)
|
|
1994
|
+
if (chunk) {
|
|
1938
1995
|
bracketedPasteBuffer += chunk;
|
|
1996
|
+
armPasteGuard();
|
|
1997
|
+
}
|
|
1939
1998
|
return true;
|
|
1940
1999
|
};
|
|
1941
2000
|
const markPasteBurst = () => {
|