@oxecli/oxe 1.0.101 → 1.0.103

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 +44 -26
  2. package/package.json +1 -1
package/dist/ui.js CHANGED
@@ -750,46 +750,64 @@ function dockRenderBox() {
750
750
  /**
751
751
  * Redraw the docked prompt box with the given buffer/caret, keeping whatever
752
752
  * content already streams above it. Used to make the prompt field live-edit
753
- * while a query runs. Erases the box, writes the new frame, and parks the
754
- * cursor at the caret. Preserves exactly one blank row between the last content
755
- * and the box. When a transient (thinking/working spinner) is parked above the
756
- * box, it is rebuilt above the new frame and the physical cursor stays on that
757
- * transient row, so streaming content is never pulled into the prompt.
753
+ * while a query runs. Preserves exactly one blank row between the last content
754
+ * and the box. Rows that did not change (top border, hint row) are left
755
+ * untouched, so static parts never flicker on each keystroke. When a transient
756
+ * (thinking/working spinner) is parked above the box, the physical cursor stays
757
+ * on that transient row, so streaming content is never pulled into the prompt
758
+ * and the box does not drift.
758
759
  */
759
760
  export function dockRenderPrompt(buffer, pasteSpans, prefix, label, hints) {
760
761
  if (!docked || dockBoxRows < 1)
761
762
  return;
762
- // If a thinking/working spinner is currently parked above the box, typing
763
- // must NOT disturb it. dockEraseBox() wipes that transient row and resets
764
- // its state; the still-running spinner then keeps drawing at the physical
765
- // caret (now inside the box), pulling streaming content into the prompt.
766
- // Capture the transient before erasing so we can rebuild it above the frame
767
- // and park the cursor back on the transient row, keeping the box intact.
768
763
  const wasTransient = dockTransient;
769
764
  const wasRel = dockRel;
765
+ const wasCaret = dockCaretBelow;
770
766
  const { frame, cursorRow, cursorCol, totalRows } = renderBufferWithCursor(buffer, pasteSpans, prefix, label, buffer.length, hints);
771
- dockEraseBox();
767
+ const newLines = frame.split("\n");
768
+ const oldLines = dockFrameLines.length ? dockFrameLines : newLines;
769
+ // Move the caret to the box's top row (R). The physical cursor is either on
770
+ // the transient row (R - wasRel) or at the live caret (R + wasCaret).
771
+ if (wasTransient)
772
+ process.stdout.write(`\x1b[${wasRel}B\r`);
773
+ else if (wasCaret > 0)
774
+ process.stdout.write(`\x1b[${wasCaret}A\r`);
775
+ // In-place line diff: rewrite only lines that actually changed (the body),
776
+ // leaving unchanged rows (top border, hint row) untouched so they never
777
+ // flicker. Clear each rewritten row so a shorter line leaves no residue.
778
+ const maxLen = Math.max(oldLines.length, newLines.length);
779
+ for (let i = 0; i < maxLen; i++) {
780
+ if (newLines[i] !== oldLines[i]) {
781
+ process.stdout.write("\r\x1b[K" + (newLines[i] ?? ""));
782
+ }
783
+ if (i < maxLen - 1)
784
+ process.stdout.write("\n");
785
+ }
786
+ // Reposition the caret.
787
+ if (wasTransient) {
788
+ // Park back on the transient row so the running spinner keeps drawing there.
789
+ process.stdout.write(`\x1b[${maxLen - 1 + wasRel}A\r`);
790
+ }
791
+ else {
792
+ const atLine = maxLen - 1;
793
+ if (atLine > cursorRow)
794
+ process.stdout.write(`\x1b[${atLine - cursorRow}A\r`);
795
+ else if (atLine < cursorRow)
796
+ process.stdout.write(`\x1b[${cursorRow - atLine}B\r`);
797
+ process.stdout.write(`\x1b[${cursorCol}C`);
798
+ }
799
+ dockSetFrame(frame, totalRows);
772
800
  if (wasTransient) {
773
- // Rebuild the transient row(s) and gap above the new frame, then park the
774
- // physical cursor at the transient row so the running spinner keeps
775
- // drawing THERE instead of inside the prompt box.
776
- process.stdout.write("\n".repeat(wasRel) + frame);
777
- process.stdout.write(`\x1b[${totalRows - 1 + wasRel}A\r`);
778
- dockSetFrame(frame, totalRows);
779
801
  docked = true;
780
802
  dockGap = false;
781
803
  dockTransient = true;
782
804
  dockRel = wasRel;
783
805
  dockCaretBelow = 0;
784
- return;
785
806
  }
786
- process.stdout.write(frame);
787
- // Park the cursor at the caret inside the box (cursorRow is 1-indexed, so
788
- // the caret sits cursorRow rows below boxTop).
789
- process.stdout.write(`\x1b[${totalRows - 1 - cursorRow}A\r\x1b[${cursorCol}C`);
790
- dockSetFrame(frame, totalRows);
791
- dockEnterDocked();
792
- dockCaretBelow = cursorRow;
807
+ else {
808
+ dockEnterDocked();
809
+ dockCaretBelow = cursorRow;
810
+ }
793
811
  }
794
812
  /** Commit content at the box top, pushing the box down, preserving exactly one
795
813
  * blank row between the content and the box. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.101",
3
+ "version": "1.0.103",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },