@oxecli/oxe 1.0.103 → 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 (3) hide show
  1. package/README.md +3 -1
  2. package/dist/ui.js +33 -33
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -20,7 +20,9 @@ Install Oxe globally with npm:
20
20
 
21
21
 
22
22
 
23
- Features
23
+ **Features**
24
+
25
+
24
26
 
25
27
  * Terminal-native — Work directly from your CLI.
26
28
  * Codebase-aware — Inspect and understand your project before making changes.
package/dist/ui.js CHANGED
@@ -667,13 +667,17 @@ export function dockTransientStart(mode = "blank") {
667
667
  return;
668
668
  dockEraseBox();
669
669
  if (mode === "flush") {
670
- // Park on the gap row directly under the preceding content (adjacent).
670
+ // Park directly under the preceding content (adjacent, no blank above), but
671
+ // still keep one blank row between the transient and the box — otherwise the
672
+ // working line (and the tool result that replaces it) sits flush against the
673
+ // prompt box. dockRel=2 mirrors the blank mode's spacing below the row.
671
674
  process.stdout.write("\x1b[1A\r");
672
675
  process.stdout.write("\n");
676
+ process.stdout.write("\n");
673
677
  process.stdout.write(dockFrameLines.join("\n"));
674
678
  process.stdout.write(`\x1b[${dockBoxRows - 1}A`);
675
- process.stdout.write("\x1b[1A\r");
676
- dockRel = 1;
679
+ process.stdout.write("\x1b[2A\r");
680
+ dockRel = 2;
677
681
  }
678
682
  else {
679
683
  // Park on a fresh spinner row that keeps a blank above and below it.
@@ -1424,6 +1428,8 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1424
1428
  : value;
1425
1429
  process.stdout.write("\x1b[?2004l");
1426
1430
  process.stdin.setRawMode(false);
1431
+ bracketedPaste = false;
1432
+ bracketedPasteBuffer = "";
1427
1433
  if (pasteBurstTimer)
1428
1434
  clearTimeout(pasteBurstTimer);
1429
1435
  if (isErr) {
@@ -1712,18 +1718,17 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1712
1718
  const end = "\x1b[201~";
1713
1719
  const sequence = key?.sequence || "";
1714
1720
  const value = str || "";
1715
- 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)) {
1716
1725
  bracketedPaste = true;
1717
1726
  bracketedPasteBuffer = "";
1718
1727
  const inline = value === start ? "" : value.startsWith(start) ? value.slice(start.length) : "";
1719
1728
  if (inline)
1720
1729
  bracketedPasteBuffer = inline;
1721
1730
  if (inline.includes(end)) {
1722
- const endAt = inline.indexOf(end);
1723
- const pasted = inline
1724
- .slice(0, endAt)
1725
- .replace(/\r\n/g, "\n")
1726
- .replace(/\r/g, "\n");
1731
+ const pasted = inline.slice(0, inline.indexOf(end)).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1727
1732
  bracketedPaste = false;
1728
1733
  bracketedPasteBuffer = "";
1729
1734
  if (pasted)
@@ -1734,7 +1739,12 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1734
1739
  }
1735
1740
  if (!bracketedPaste)
1736
1741
  return false;
1737
- 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) : "";
1738
1748
  const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1739
1749
  bracketedPaste = false;
1740
1750
  bracketedPasteBuffer = "";
@@ -1744,21 +1754,8 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1744
1754
  return true;
1745
1755
  }
1746
1756
  const chunk = value || (sequence && !sequence.startsWith("\x1b[") ? sequence : "");
1747
- if (chunk) {
1748
- const endAt = chunk.indexOf(end);
1749
- if (endAt >= 0) {
1750
- bracketedPasteBuffer += chunk.slice(0, endAt);
1751
- const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
1752
- bracketedPaste = false;
1753
- bracketedPasteBuffer = "";
1754
- if (pasted)
1755
- insert(pasted, true);
1756
- repaint();
1757
- }
1758
- else {
1759
- bracketedPasteBuffer += chunk;
1760
- }
1761
- }
1757
+ if (chunk)
1758
+ bracketedPasteBuffer += chunk;
1762
1759
  return true;
1763
1760
  };
1764
1761
  const markPasteBurst = () => {
@@ -1771,14 +1768,9 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1771
1768
  }, 500);
1772
1769
  };
1773
1770
  const onKeypress = (str, key) => {
1774
- if (consumeBracketedPaste(str, key))
1775
- return;
1776
- if (pasteBurst && str && str.length === 1 && !key?.ctrl && !key?.meta) {
1777
- pasteBurst = false;
1778
- if (pasteBurstTimer)
1779
- clearTimeout(pasteBurstTimer);
1780
- pasteBurstTimer = null;
1781
- }
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.
1782
1774
  if (key && key.ctrl && key.name === "c") {
1783
1775
  settle(new Error("interrupt"), true);
1784
1776
  return;
@@ -1787,6 +1779,14 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = [], hin
1787
1779
  settle(new Error("eof"), true);
1788
1780
  return;
1789
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
+ }
1790
1790
  if (key && key.ctrl && key.name === "a") {
1791
1791
  cursor = 0;
1792
1792
  repaint();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.103",
3
+ "version": "1.0.105",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },