u-foo 2.3.12 → 2.3.14
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/package.json +1 -1
- package/src/agent/defaultBootstrap.js +28 -3
- package/src/agent/internalRunner.js +126 -124
- package/src/agent/ptyRunner.js +70 -8
- package/src/agent/ucodeBootstrap.js +23 -1
- package/src/agent/ufooAgent.js +7 -36
- package/src/chat/daemonMessageRouter.js +3 -1
- package/src/chat/dashboardKeyController.js +1 -1
- package/src/chat/dashboardView.js +1 -1
- package/src/chat/index.js +1 -1
- package/src/chat/inputListenerController.js +196 -54
- package/src/chat/layout.js +18 -2
- package/src/code/tui.js +405 -50
- package/src/config.js +1 -0
- package/src/daemon/ops.js +57 -12
- package/src/agent/cliRunner.js +0 -706
- package/src/agent/normalizeOutput.js +0 -41
package/src/code/tui.js
CHANGED
|
@@ -293,6 +293,111 @@ function moveCursorHorizontally(cursorPos = 0, inputValue = "", direction = "rig
|
|
|
293
293
|
return Math.min(max, pos + 1);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
+
function clampCursorPos(cursorPos = 0, inputValue = "") {
|
|
297
|
+
const text = String(inputValue || "");
|
|
298
|
+
const pos = Number.isFinite(cursorPos) ? Math.floor(cursorPos) : 0;
|
|
299
|
+
return Math.max(0, Math.min(text.length, pos));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function findLogicalLineStart(inputValue = "", cursorPos = 0) {
|
|
303
|
+
const text = String(inputValue || "");
|
|
304
|
+
const pos = clampCursorPos(cursorPos, text);
|
|
305
|
+
const prevNewline = text.lastIndexOf("\n", Math.max(0, pos - 1));
|
|
306
|
+
return prevNewline === -1 ? 0 : prevNewline + 1;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function findLogicalLineEnd(inputValue = "", cursorPos = 0) {
|
|
310
|
+
const text = String(inputValue || "");
|
|
311
|
+
const pos = clampCursorPos(cursorPos, text);
|
|
312
|
+
const nextNewline = text.indexOf("\n", pos);
|
|
313
|
+
return nextNewline === -1 ? text.length : nextNewline;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function moveCursorToVisualLineBoundary({
|
|
317
|
+
cursorPos = 0,
|
|
318
|
+
inputValue = "",
|
|
319
|
+
width = 80,
|
|
320
|
+
boundary = "start",
|
|
321
|
+
strWidth,
|
|
322
|
+
} = {}) {
|
|
323
|
+
const inputMath = require("../chat/inputMath");
|
|
324
|
+
const text = String(inputValue || "");
|
|
325
|
+
const normalizedWidth = Number.isFinite(width) ? Math.max(1, Math.floor(width)) : 1;
|
|
326
|
+
const pos = clampCursorPos(cursorPos, text);
|
|
327
|
+
const { row } = inputMath.getCursorRowCol(text, pos, normalizedWidth, strWidth);
|
|
328
|
+
if (boundary === "end") {
|
|
329
|
+
return inputMath.getCursorPosForRowCol(text, row, normalizedWidth, normalizedWidth, strWidth);
|
|
330
|
+
}
|
|
331
|
+
return inputMath.getCursorPosForRowCol(text, row, 0, normalizedWidth, strWidth);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function moveCursorVertically({
|
|
335
|
+
cursorPos = 0,
|
|
336
|
+
inputValue = "",
|
|
337
|
+
width = 80,
|
|
338
|
+
direction = "down",
|
|
339
|
+
preferredCol = null,
|
|
340
|
+
strWidth,
|
|
341
|
+
} = {}) {
|
|
342
|
+
const inputMath = require("../chat/inputMath");
|
|
343
|
+
const text = String(inputValue || "");
|
|
344
|
+
const normalizedWidth = Number.isFinite(width) ? Math.max(1, Math.floor(width)) : 1;
|
|
345
|
+
const pos = clampCursorPos(cursorPos, text);
|
|
346
|
+
const { row, col } = inputMath.getCursorRowCol(text, pos, normalizedWidth, strWidth);
|
|
347
|
+
const totalRows = inputMath.countLines(text, normalizedWidth, strWidth);
|
|
348
|
+
const targetCol = Number.isFinite(preferredCol) ? preferredCol : col;
|
|
349
|
+
|
|
350
|
+
if (direction === "up") {
|
|
351
|
+
if (row <= 0) {
|
|
352
|
+
return { moved: false, nextCursorPos: pos, preferredCol: targetCol, boundary: "top" };
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
moved: true,
|
|
356
|
+
nextCursorPos: inputMath.getCursorPosForRowCol(text, row - 1, targetCol, normalizedWidth, strWidth),
|
|
357
|
+
preferredCol: targetCol,
|
|
358
|
+
boundary: "",
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (row >= totalRows - 1) {
|
|
363
|
+
return { moved: false, nextCursorPos: pos, preferredCol: targetCol, boundary: "bottom" };
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
moved: true,
|
|
367
|
+
nextCursorPos: inputMath.getCursorPosForRowCol(text, row + 1, targetCol, normalizedWidth, strWidth),
|
|
368
|
+
preferredCol: targetCol,
|
|
369
|
+
boundary: "",
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function deleteWordBeforeCursor(inputValue = "", cursorPos = 0) {
|
|
374
|
+
const text = String(inputValue || "");
|
|
375
|
+
const pos = clampCursorPos(cursorPos, text);
|
|
376
|
+
if (pos <= 0) return { value: text, cursorPos: pos };
|
|
377
|
+
const before = text.slice(0, pos);
|
|
378
|
+
const after = text.slice(pos);
|
|
379
|
+
const match = before.match(/\s*\S+\s*$/);
|
|
380
|
+
const start = match ? pos - match[0].length : Math.max(0, pos - 1);
|
|
381
|
+
return {
|
|
382
|
+
value: before.slice(0, start) + after,
|
|
383
|
+
cursorPos: start,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function moveCursorByWord(inputValue = "", cursorPos = 0, direction = "forward") {
|
|
388
|
+
const text = String(inputValue || "");
|
|
389
|
+
const pos = clampCursorPos(cursorPos, text);
|
|
390
|
+
if (direction === "backward") {
|
|
391
|
+
const before = text.slice(0, pos);
|
|
392
|
+
const trimmedEnd = before.search(/\S\s*$/) >= 0 ? before.replace(/\s+$/, "") : before;
|
|
393
|
+
const match = trimmedEnd.match(/\S+$/);
|
|
394
|
+
return match ? trimmedEnd.length - match[0].length : 0;
|
|
395
|
+
}
|
|
396
|
+
const after = text.slice(pos);
|
|
397
|
+
const match = after.match(/^\s*\S+/);
|
|
398
|
+
return match ? Math.min(text.length, pos + match[0].length) : text.length;
|
|
399
|
+
}
|
|
400
|
+
|
|
296
401
|
function resolveHistoryDownTransition({
|
|
297
402
|
inputHistory = [],
|
|
298
403
|
historyIndex = 0,
|
|
@@ -499,6 +604,11 @@ function runUcodeTui({
|
|
|
499
604
|
let lastMergedToolGroup = null;
|
|
500
605
|
let toolMergeId = 0;
|
|
501
606
|
let cursorPos = 0;
|
|
607
|
+
let preferredCol = null;
|
|
608
|
+
let currentInputHeight = 4;
|
|
609
|
+
const MIN_INPUT_CONTENT_HEIGHT = 1;
|
|
610
|
+
const MAX_INPUT_CONTENT_HEIGHT = 8;
|
|
611
|
+
const DASHBOARD_HEIGHT = 1;
|
|
502
612
|
let autoBusTimer = null;
|
|
503
613
|
let autoBusQueued = false;
|
|
504
614
|
let autoBusError = "";
|
|
@@ -510,12 +620,15 @@ function runUcodeTui({
|
|
|
510
620
|
statusLine,
|
|
511
621
|
completionPanel,
|
|
512
622
|
dashboard,
|
|
623
|
+
inputTopLine,
|
|
513
624
|
promptBox,
|
|
514
625
|
input,
|
|
515
626
|
} = createChatLayout({
|
|
516
627
|
blessed,
|
|
517
628
|
currentInputHeight: 4,
|
|
518
629
|
version: UCODE_VERSION,
|
|
630
|
+
logBorder: true,
|
|
631
|
+
logScrollbar: true,
|
|
519
632
|
});
|
|
520
633
|
|
|
521
634
|
if (completionPanel && typeof completionPanel.hide === "function") {
|
|
@@ -555,6 +668,7 @@ function runUcodeTui({
|
|
|
555
668
|
promptBox.width = Math.max(2, plain.length + 1);
|
|
556
669
|
input.left = promptBox.width;
|
|
557
670
|
input.width = `100%-${promptBox.width}`;
|
|
671
|
+
resizeInput();
|
|
558
672
|
};
|
|
559
673
|
|
|
560
674
|
// --- Cursor position helpers (mirrors chat inputListenerController) ---
|
|
@@ -565,6 +679,10 @@ function runUcodeTui({
|
|
|
565
679
|
|
|
566
680
|
const getWrapWidth = () => inputMath.getWrapWidth(input, getInnerWidth());
|
|
567
681
|
|
|
682
|
+
const resetPreferredCol = () => {
|
|
683
|
+
preferredCol = null;
|
|
684
|
+
};
|
|
685
|
+
|
|
568
686
|
const ensureInputCursorVisible = () => {
|
|
569
687
|
const innerWidth = getWrapWidth();
|
|
570
688
|
if (innerWidth <= 0) return;
|
|
@@ -583,6 +701,105 @@ function runUcodeTui({
|
|
|
583
701
|
}
|
|
584
702
|
};
|
|
585
703
|
|
|
704
|
+
const resizeInput = () => {
|
|
705
|
+
const innerWidth = getWrapWidth();
|
|
706
|
+
if (innerWidth <= 0) return;
|
|
707
|
+
const totalRows = inputMath.countLines(input.value || "", innerWidth, (v) => input.strWidth(v));
|
|
708
|
+
const contentHeight = Math.min(
|
|
709
|
+
MAX_INPUT_CONTENT_HEIGHT,
|
|
710
|
+
Math.max(MIN_INPUT_CONTENT_HEIGHT, totalRows)
|
|
711
|
+
);
|
|
712
|
+
const targetHeight = contentHeight + DASHBOARD_HEIGHT + 2;
|
|
713
|
+
if (targetHeight !== currentInputHeight) {
|
|
714
|
+
currentInputHeight = targetHeight;
|
|
715
|
+
input.height = contentHeight;
|
|
716
|
+
promptBox.height = contentHeight;
|
|
717
|
+
if (inputTopLine) inputTopLine.bottom = currentInputHeight - 1;
|
|
718
|
+
}
|
|
719
|
+
statusLine.bottom = currentInputHeight;
|
|
720
|
+
logBox.height = Math.max(1, screen.height - currentInputHeight - 1);
|
|
721
|
+
ensureInputCursorVisible();
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
const renderInput = () => {
|
|
725
|
+
resizeInput();
|
|
726
|
+
ensureInputCursorVisible();
|
|
727
|
+
input._updateCursor();
|
|
728
|
+
screen.render();
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
const setCursor = (nextPos) => {
|
|
732
|
+
cursorPos = clampCursorPos(nextPos, input.value || "");
|
|
733
|
+
ensureInputCursorVisible();
|
|
734
|
+
input._updateCursor();
|
|
735
|
+
screen.render();
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
const setInputValue = (value) => {
|
|
739
|
+
input.setValue(value || "");
|
|
740
|
+
cursorPos = (value || "").length;
|
|
741
|
+
resetPreferredCol();
|
|
742
|
+
renderInput();
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
const replaceInputRange = (start, end, replacement = "") => {
|
|
746
|
+
const value = input.value || "";
|
|
747
|
+
const safeStart = clampCursorPos(start, value);
|
|
748
|
+
const safeEnd = clampCursorPos(end, value);
|
|
749
|
+
const from = Math.min(safeStart, safeEnd);
|
|
750
|
+
const to = Math.max(safeStart, safeEnd);
|
|
751
|
+
input.value = value.slice(0, from) + String(replacement || "") + value.slice(to);
|
|
752
|
+
cursorPos = from + String(replacement || "").length;
|
|
753
|
+
resetPreferredCol();
|
|
754
|
+
renderInput();
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
const insertTextAtCursor = (text = "") => {
|
|
758
|
+
const normalized = inputMath.normalizePaste(text);
|
|
759
|
+
if (!normalized) return;
|
|
760
|
+
replaceInputRange(cursorPos, cursorPos, normalized);
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
const deleteBeforeCursor = () => {
|
|
764
|
+
if (cursorPos <= 0) return;
|
|
765
|
+
replaceInputRange(cursorPos - 1, cursorPos, "");
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
const deleteAtCursor = () => {
|
|
769
|
+
const value = input.value || "";
|
|
770
|
+
if (cursorPos >= value.length) return;
|
|
771
|
+
replaceInputRange(cursorPos, cursorPos + 1, "");
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
const deleteToBoundary = (boundary) => {
|
|
775
|
+
const value = input.value || "";
|
|
776
|
+
const innerWidth = getWrapWidth();
|
|
777
|
+
const target = boundary === "end"
|
|
778
|
+
? moveCursorToVisualLineBoundary({
|
|
779
|
+
cursorPos,
|
|
780
|
+
inputValue: value,
|
|
781
|
+
width: innerWidth,
|
|
782
|
+
boundary: "end",
|
|
783
|
+
strWidth: (v) => input.strWidth(v),
|
|
784
|
+
})
|
|
785
|
+
: moveCursorToVisualLineBoundary({
|
|
786
|
+
cursorPos,
|
|
787
|
+
inputValue: value,
|
|
788
|
+
width: innerWidth,
|
|
789
|
+
boundary: "start",
|
|
790
|
+
strWidth: (v) => input.strWidth(v),
|
|
791
|
+
});
|
|
792
|
+
if (target === cursorPos && boundary === "end" && value[cursorPos] === "\n") {
|
|
793
|
+
replaceInputRange(cursorPos, cursorPos + 1, "");
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
if (target === cursorPos && boundary === "start" && value[cursorPos - 1] === "\n") {
|
|
797
|
+
replaceInputRange(cursorPos - 1, cursorPos, "");
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
replaceInputRange(Math.min(cursorPos, target), Math.max(cursorPos, target), "");
|
|
801
|
+
};
|
|
802
|
+
|
|
586
803
|
// Override _updateCursor to use our tracked cursorPos
|
|
587
804
|
input._updateCursor = function () {
|
|
588
805
|
if (this.screen.focused !== this) return;
|
|
@@ -603,8 +820,8 @@ function runUcodeTui({
|
|
|
603
820
|
};
|
|
604
821
|
|
|
605
822
|
// Override _listener to support cursor-aware editing
|
|
606
|
-
const origDone = input._done ? input._done.bind(input) : null;
|
|
607
823
|
let lastKeyRef = null;
|
|
824
|
+
let skipSubmitKeyRef = null;
|
|
608
825
|
input._listener = function (ch, key) {
|
|
609
826
|
const keyName = key && key.name;
|
|
610
827
|
|
|
@@ -614,69 +831,161 @@ function runUcodeTui({
|
|
|
614
831
|
if (key && key === lastKeyRef) return;
|
|
615
832
|
lastKeyRef = key || null;
|
|
616
833
|
|
|
617
|
-
|
|
618
|
-
|
|
834
|
+
if (keyName === "escape") return;
|
|
835
|
+
|
|
836
|
+
if (keyName === "return" || keyName === "enter") {
|
|
837
|
+
const value = this.value || "";
|
|
838
|
+
if (key && (key.shift || key.meta)) {
|
|
839
|
+
insertTextAtCursor("\n");
|
|
840
|
+
skipSubmitKeyRef = key || true;
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
if (cursorPos > 0 && value[cursorPos - 1] === "\\") {
|
|
844
|
+
replaceInputRange(cursorPos - 1, cursorPos, "\n");
|
|
845
|
+
skipSubmitKeyRef = key || true;
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
619
850
|
|
|
620
851
|
// Arrow keys handled by input.key() handlers below
|
|
621
852
|
if (keyName === "left" || keyName === "right" || keyName === "up" || keyName === "down") return;
|
|
622
853
|
|
|
854
|
+
if (key && key.ctrl) {
|
|
855
|
+
if (keyName === "a") {
|
|
856
|
+
setCursor(moveCursorToVisualLineBoundary({
|
|
857
|
+
cursorPos,
|
|
858
|
+
inputValue: this.value || "",
|
|
859
|
+
width: getWrapWidth(),
|
|
860
|
+
boundary: "start",
|
|
861
|
+
strWidth: (v) => this.strWidth(v),
|
|
862
|
+
}));
|
|
863
|
+
resetPreferredCol();
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
if (keyName === "e") {
|
|
867
|
+
setCursor(moveCursorToVisualLineBoundary({
|
|
868
|
+
cursorPos,
|
|
869
|
+
inputValue: this.value || "",
|
|
870
|
+
width: getWrapWidth(),
|
|
871
|
+
boundary: "end",
|
|
872
|
+
strWidth: (v) => this.strWidth(v),
|
|
873
|
+
}));
|
|
874
|
+
resetPreferredCol();
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
if (keyName === "b") {
|
|
878
|
+
setCursor(moveCursorHorizontally(cursorPos, this.value || "", "left"));
|
|
879
|
+
resetPreferredCol();
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
if (keyName === "f") {
|
|
883
|
+
setCursor(moveCursorHorizontally(cursorPos, this.value || "", "right"));
|
|
884
|
+
resetPreferredCol();
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (keyName === "d") {
|
|
888
|
+
deleteAtCursor();
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
if (keyName === "h") {
|
|
892
|
+
deleteBeforeCursor();
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
if (keyName === "k") {
|
|
896
|
+
deleteToBoundary("end");
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
if (keyName === "u") {
|
|
900
|
+
deleteToBoundary("start");
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
if (keyName === "w") {
|
|
904
|
+
const next = deleteWordBeforeCursor(this.value || "", cursorPos);
|
|
905
|
+
this.value = next.value;
|
|
906
|
+
cursorPos = next.cursorPos;
|
|
907
|
+
resetPreferredCol();
|
|
908
|
+
renderInput();
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (key && key.meta) {
|
|
914
|
+
if (keyName === "b") {
|
|
915
|
+
setCursor(moveCursorByWord(this.value || "", cursorPos, "backward"));
|
|
916
|
+
resetPreferredCol();
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
if (keyName === "f") {
|
|
920
|
+
setCursor(moveCursorByWord(this.value || "", cursorPos, "forward"));
|
|
921
|
+
resetPreferredCol();
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
if (keyName === "d") {
|
|
925
|
+
const end = moveCursorByWord(this.value || "", cursorPos, "forward");
|
|
926
|
+
replaceInputRange(cursorPos, end, "");
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
623
931
|
if (keyName === "backspace") {
|
|
624
|
-
if (
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
932
|
+
if (key && (key.meta || key.ctrl)) {
|
|
933
|
+
const next = deleteWordBeforeCursor(this.value || "", cursorPos);
|
|
934
|
+
this.value = next.value;
|
|
935
|
+
cursorPos = next.cursorPos;
|
|
936
|
+
resetPreferredCol();
|
|
937
|
+
renderInput();
|
|
938
|
+
} else {
|
|
939
|
+
deleteBeforeCursor();
|
|
630
940
|
}
|
|
631
941
|
return;
|
|
632
942
|
}
|
|
633
943
|
|
|
634
944
|
if (keyName === "delete") {
|
|
635
|
-
if (
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
this.screen.render();
|
|
945
|
+
if (key && key.meta) {
|
|
946
|
+
deleteToBoundary("end");
|
|
947
|
+
} else {
|
|
948
|
+
deleteAtCursor();
|
|
640
949
|
}
|
|
641
950
|
return;
|
|
642
951
|
}
|
|
643
952
|
|
|
644
953
|
if (keyName === "home") {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
954
|
+
setCursor(moveCursorToVisualLineBoundary({
|
|
955
|
+
cursorPos,
|
|
956
|
+
inputValue: this.value || "",
|
|
957
|
+
width: getWrapWidth(),
|
|
958
|
+
boundary: "start",
|
|
959
|
+
strWidth: (v) => this.strWidth(v),
|
|
960
|
+
}));
|
|
961
|
+
resetPreferredCol();
|
|
649
962
|
return;
|
|
650
963
|
}
|
|
651
964
|
|
|
652
965
|
if (keyName === "end") {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
966
|
+
setCursor(moveCursorToVisualLineBoundary({
|
|
967
|
+
cursorPos,
|
|
968
|
+
inputValue: this.value || "",
|
|
969
|
+
width: getWrapWidth(),
|
|
970
|
+
boundary: "end",
|
|
971
|
+
strWidth: (v) => this.strWidth(v),
|
|
972
|
+
}));
|
|
973
|
+
resetPreferredCol();
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
if (ch && ch.length > 1 && (!keyName || keyName.length !== 1)) {
|
|
978
|
+
insertTextAtCursor(ch);
|
|
657
979
|
return;
|
|
658
980
|
}
|
|
659
981
|
|
|
660
982
|
// Normal character insertion at cursor position
|
|
661
983
|
const insertChar = (ch && ch.length === 1) ? ch : (keyName && keyName.length === 1 ? keyName : null);
|
|
662
984
|
if (insertChar && !/^[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]$/.test(insertChar)) {
|
|
663
|
-
|
|
664
|
-
cursorPos += 1;
|
|
665
|
-
ensureInputCursorVisible();
|
|
666
|
-
this._updateCursor();
|
|
667
|
-
this.screen.render();
|
|
985
|
+
insertTextAtCursor(insertChar);
|
|
668
986
|
}
|
|
669
987
|
};
|
|
670
988
|
|
|
671
|
-
// Helper to set input value and reset cursor to end
|
|
672
|
-
const setInputValue = (value) => {
|
|
673
|
-
input.setValue(value || "");
|
|
674
|
-
cursorPos = (value || "").length;
|
|
675
|
-
ensureInputCursorVisible();
|
|
676
|
-
input._updateCursor();
|
|
677
|
-
screen.render();
|
|
678
|
-
};
|
|
679
|
-
|
|
680
989
|
const renderDashboard = () => {
|
|
681
990
|
let hint = "No target agents";
|
|
682
991
|
if (activeAgents.length > 0) {
|
|
@@ -922,7 +1231,7 @@ function runUcodeTui({
|
|
|
922
1231
|
statusInterval = null;
|
|
923
1232
|
}
|
|
924
1233
|
if (!message) {
|
|
925
|
-
statusLine.setContent(escapeBlessed(`UCODE · Ready${getBackgroundSuffix()}`));
|
|
1234
|
+
statusLine.setContent(escapeBlessed(`UCODE · Ready · Enter send · Shift/Alt+Enter newline · PgUp/PgDn log · Ctrl+O tools${getBackgroundSuffix()}`));
|
|
926
1235
|
screen.render();
|
|
927
1236
|
return;
|
|
928
1237
|
}
|
|
@@ -1321,6 +1630,8 @@ function runUcodeTui({
|
|
|
1321
1630
|
const trimmed = raw.trim();
|
|
1322
1631
|
input.setValue("");
|
|
1323
1632
|
cursorPos = 0;
|
|
1633
|
+
resetPreferredCol();
|
|
1634
|
+
resizeInput();
|
|
1324
1635
|
screen.render();
|
|
1325
1636
|
agentSelectionMode = false;
|
|
1326
1637
|
|
|
@@ -1345,7 +1656,11 @@ function runUcodeTui({
|
|
|
1345
1656
|
});
|
|
1346
1657
|
};
|
|
1347
1658
|
|
|
1348
|
-
input.key(["enter"], () => {
|
|
1659
|
+
input.key(["enter"], (ch, key) => {
|
|
1660
|
+
if (skipSubmitKeyRef && (!key || skipSubmitKeyRef === key || skipSubmitKeyRef === true)) {
|
|
1661
|
+
skipSubmitKeyRef = null;
|
|
1662
|
+
return false;
|
|
1663
|
+
}
|
|
1349
1664
|
submitInput(input.getValue());
|
|
1350
1665
|
return false;
|
|
1351
1666
|
});
|
|
@@ -1355,7 +1670,6 @@ function runUcodeTui({
|
|
|
1355
1670
|
agentSelectionMode,
|
|
1356
1671
|
inputValue: currentValue,
|
|
1357
1672
|
})) {
|
|
1358
|
-
const previousTarget = targetAgent;
|
|
1359
1673
|
targetAgent = null;
|
|
1360
1674
|
selectedAgentIndex = -1;
|
|
1361
1675
|
agentSelectionMode = false;
|
|
@@ -1365,12 +1679,43 @@ function runUcodeTui({
|
|
|
1365
1679
|
input.focus();
|
|
1366
1680
|
return false;
|
|
1367
1681
|
}
|
|
1368
|
-
if (
|
|
1682
|
+
if (currentValue) {
|
|
1683
|
+
const move = moveCursorVertically({
|
|
1684
|
+
cursorPos,
|
|
1685
|
+
inputValue: currentValue,
|
|
1686
|
+
width: getWrapWidth(),
|
|
1687
|
+
direction: "up",
|
|
1688
|
+
preferredCol,
|
|
1689
|
+
strWidth: (v) => input.strWidth(v),
|
|
1690
|
+
});
|
|
1691
|
+
preferredCol = move.preferredCol;
|
|
1692
|
+
if (move.moved) {
|
|
1693
|
+
setCursor(move.nextCursorPos);
|
|
1694
|
+
return false;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (inputHistory.length === 0) return false;
|
|
1369
1698
|
historyIndex = Math.max(0, historyIndex - 1);
|
|
1370
1699
|
setInputValue(inputHistory[historyIndex] || "");
|
|
1700
|
+
return false;
|
|
1371
1701
|
});
|
|
1372
1702
|
input.key(["down"], () => {
|
|
1373
1703
|
const currentValue = input.getValue();
|
|
1704
|
+
if (currentValue) {
|
|
1705
|
+
const move = moveCursorVertically({
|
|
1706
|
+
cursorPos,
|
|
1707
|
+
inputValue: currentValue,
|
|
1708
|
+
width: getWrapWidth(),
|
|
1709
|
+
direction: "down",
|
|
1710
|
+
preferredCol,
|
|
1711
|
+
strWidth: (v) => input.strWidth(v),
|
|
1712
|
+
});
|
|
1713
|
+
preferredCol = move.preferredCol;
|
|
1714
|
+
if (move.moved) {
|
|
1715
|
+
setCursor(move.nextCursorPos);
|
|
1716
|
+
return false;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1374
1719
|
const historyTransition = resolveHistoryDownTransition({
|
|
1375
1720
|
inputHistory,
|
|
1376
1721
|
historyIndex,
|
|
@@ -1428,10 +1773,8 @@ function runUcodeTui({
|
|
|
1428
1773
|
}
|
|
1429
1774
|
const next = moveCursorHorizontally(cursorPos, currentValue, "left");
|
|
1430
1775
|
if (next !== cursorPos) {
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
input._updateCursor();
|
|
1434
|
-
screen.render();
|
|
1776
|
+
setCursor(next);
|
|
1777
|
+
resetPreferredCol();
|
|
1435
1778
|
}
|
|
1436
1779
|
return false;
|
|
1437
1780
|
});
|
|
@@ -1450,10 +1793,8 @@ function runUcodeTui({
|
|
|
1450
1793
|
}
|
|
1451
1794
|
const next = moveCursorHorizontally(cursorPos, currentValue, "right");
|
|
1452
1795
|
if (next !== cursorPos) {
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
input._updateCursor();
|
|
1456
|
-
screen.render();
|
|
1796
|
+
setCursor(next);
|
|
1797
|
+
resetPreferredCol();
|
|
1457
1798
|
}
|
|
1458
1799
|
return false;
|
|
1459
1800
|
});
|
|
@@ -1496,6 +1837,14 @@ function runUcodeTui({
|
|
|
1496
1837
|
}
|
|
1497
1838
|
screen.render();
|
|
1498
1839
|
});
|
|
1840
|
+
screen.key(["pageup"], () => {
|
|
1841
|
+
logBox.scroll(-Math.max(1, Math.floor((logBox.height || 10) / 2)));
|
|
1842
|
+
screen.render();
|
|
1843
|
+
});
|
|
1844
|
+
screen.key(["pagedown"], () => {
|
|
1845
|
+
logBox.scroll(Math.max(1, Math.floor((logBox.height || 10) / 2)));
|
|
1846
|
+
screen.render();
|
|
1847
|
+
});
|
|
1499
1848
|
input.key(["escape"], () => {
|
|
1500
1849
|
if (pendingTask && pendingTask.abortController && !pendingTask.abortController.signal.aborted) {
|
|
1501
1850
|
try {
|
|
@@ -1510,11 +1859,10 @@ function runUcodeTui({
|
|
|
1510
1859
|
});
|
|
1511
1860
|
return false;
|
|
1512
1861
|
}
|
|
1513
|
-
const previousTarget = targetAgent;
|
|
1514
1862
|
targetAgent = null;
|
|
1515
1863
|
selectedAgentIndex = -1;
|
|
1516
1864
|
agentSelectionMode = false;
|
|
1517
|
-
|
|
1865
|
+
setInputValue("");
|
|
1518
1866
|
setPrompt();
|
|
1519
1867
|
renderDashboard();
|
|
1520
1868
|
// Target selection cleared - removed redundant log
|
|
@@ -1573,6 +1921,13 @@ module.exports = {
|
|
|
1573
1921
|
cycleAgentSelectionIndex,
|
|
1574
1922
|
shouldClearAgentSelectionOnUp,
|
|
1575
1923
|
moveCursorHorizontally,
|
|
1924
|
+
clampCursorPos,
|
|
1925
|
+
findLogicalLineStart,
|
|
1926
|
+
findLogicalLineEnd,
|
|
1927
|
+
moveCursorToVisualLineBoundary,
|
|
1928
|
+
moveCursorVertically,
|
|
1929
|
+
deleteWordBeforeCursor,
|
|
1930
|
+
moveCursorByWord,
|
|
1576
1931
|
resolveHistoryDownTransition,
|
|
1577
1932
|
filterSelectableAgents,
|
|
1578
1933
|
stripLeakedEscapeTags,
|
package/src/config.js
CHANGED
|
@@ -29,6 +29,7 @@ const DEFAULT_UCODE_CONFIG = {
|
|
|
29
29
|
function normalizeLaunchMode(value) {
|
|
30
30
|
if (value === "auto") return "auto";
|
|
31
31
|
if (value === "internal") return "internal";
|
|
32
|
+
if (value === "internal-pty") return "internal-pty";
|
|
32
33
|
if (value === "tmux") return "tmux";
|
|
33
34
|
if (value === "terminal") return "terminal";
|
|
34
35
|
if (value === "host") return "host";
|