@f5xc-salesdemos/pi-tui 18.43.0 → 18.44.0
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 +3 -3
- package/src/components/editor.ts +39 -3
- package/src/tui.ts +7 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/pi-tui",
|
|
4
|
-
"version": "18.
|
|
4
|
+
"version": "18.44.0",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://github.com/f5xc-salesdemos/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"fmt": "biome format --write ."
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@f5xc-salesdemos/pi-natives": "18.
|
|
41
|
-
"@f5xc-salesdemos/pi-utils": "18.
|
|
40
|
+
"@f5xc-salesdemos/pi-natives": "18.44.0",
|
|
41
|
+
"@f5xc-salesdemos/pi-utils": "18.44.0",
|
|
42
42
|
"marked": "^17.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
package/src/components/editor.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getProjectDir, logger } from "@f5xc-salesdemos/pi-utils";
|
|
1
|
+
import { $flag, getProjectDir, logger } from "@f5xc-salesdemos/pi-utils";
|
|
2
2
|
import type { AutocompleteProvider, CombinedAutocompleteProvider } from "../autocomplete";
|
|
3
3
|
import { BracketedPasteHandler } from "../bracketed-paste";
|
|
4
4
|
import { getKeybindings, type KeybindingsManager } from "../keybindings";
|
|
@@ -807,6 +807,13 @@ export class Editor implements Component, Focusable {
|
|
|
807
807
|
displayWidth += visibleWidth(inlineHint);
|
|
808
808
|
} else if (after.length === 0 && !borderVisible && displayWidth >= lineContentWidth) {
|
|
809
809
|
displayText = this.#renderTerminalCursorMarker(before, marker, lineContentWidth);
|
|
810
|
+
} else if (borderVisible && after.length === 0 && displayWidth <= lineContentWidth) {
|
|
811
|
+
const { text: cursorGlyph, width: cursorGlyphWidth } = this.#getStyledInputCursor();
|
|
812
|
+
displayText = before + marker + cursorGlyph;
|
|
813
|
+
displayWidth += cursorGlyphWidth;
|
|
814
|
+
if (displayWidth > lineContentWidth && paddingX > 0) {
|
|
815
|
+
cursorInPadding = true;
|
|
816
|
+
}
|
|
810
817
|
} else {
|
|
811
818
|
displayText = before + marker + after;
|
|
812
819
|
}
|
|
@@ -879,16 +886,22 @@ export class Editor implements Component, Focusable {
|
|
|
879
886
|
// All lines have consistent borders based on padding
|
|
880
887
|
const isLastLine = visibleIndex === visibleLayoutLines.length - 1;
|
|
881
888
|
const rightPaddingWidth = Math.max(0, paddingX - (cursorInPadding ? 1 : 0));
|
|
889
|
+
// When the hardware cursor sits on the padding space before the border,
|
|
890
|
+
// the border's ANSI foreground color can make the cursor invisible.
|
|
891
|
+
// Insert a reset before the border so the cursor uses default colors.
|
|
892
|
+
const cursorAtEnd =
|
|
893
|
+
hasCursor && this.#useTerminalCursor && (layoutLine.cursorPos ?? 0) >= layoutLine.text.length;
|
|
894
|
+
const padReset = cursorAtEnd ? "\x1b[0m" : "";
|
|
882
895
|
if (isLastLine) {
|
|
883
896
|
const bottomRightPadding = Math.max(0, paddingX - 1 - (cursorInPadding ? 1 : 0));
|
|
884
897
|
const bottomRightAdjusted = this.borderColor(
|
|
885
898
|
`${padding(bottomRightPadding)}${box.horizontal}${box.bottomRight}`,
|
|
886
899
|
);
|
|
887
|
-
result.push(`${bottomLeft}${displayText}${linePad}${bottomRightAdjusted}`);
|
|
900
|
+
result.push(`${bottomLeft}${displayText}${linePad}${padReset}${bottomRightAdjusted}`);
|
|
888
901
|
} else {
|
|
889
902
|
const leftBorder = this.borderColor(`${box.vertical}${padding(paddingX)}`);
|
|
890
903
|
const rightBorder = this.borderColor(`${padding(rightPaddingWidth)}${box.vertical}`);
|
|
891
|
-
result.push(leftBorder + displayText + linePad + rightBorder);
|
|
904
|
+
result.push(leftBorder + displayText + linePad + padReset + rightBorder);
|
|
892
905
|
}
|
|
893
906
|
}
|
|
894
907
|
|
|
@@ -898,6 +911,29 @@ export class Editor implements Component, Focusable {
|
|
|
898
911
|
result.push(...autocompleteResult);
|
|
899
912
|
}
|
|
900
913
|
|
|
914
|
+
// Guard: clamp any oversized lines to prevent ANSI corruption from terminal wrapping
|
|
915
|
+
// Strip CURSOR_MARKER before measuring — it's an APC sequence that visibleWidth
|
|
916
|
+
// miscounts as visible chars; the TUI strips it later in #extractCursorPosition.
|
|
917
|
+
for (let i = 0; i < result.length; i++) {
|
|
918
|
+
const stripped = result[i].replaceAll(CURSOR_MARKER, "");
|
|
919
|
+
const w = visibleWidth(stripped);
|
|
920
|
+
if (w > width) {
|
|
921
|
+
if ($flag("PI_DEBUG_EDITOR_WIDTH")) {
|
|
922
|
+
const section =
|
|
923
|
+
i === 0 && borderVisible
|
|
924
|
+
? "top-border"
|
|
925
|
+
: i === result.length - 1 && borderVisible
|
|
926
|
+
? "bottom-border"
|
|
927
|
+
: borderVisible
|
|
928
|
+
? "middle-line"
|
|
929
|
+
: "borderless";
|
|
930
|
+
logger.warn(`Editor line ${i} exceeds width (${w} > ${width}) in ${section}`);
|
|
931
|
+
}
|
|
932
|
+
const hadMarker = result[i] !== stripped;
|
|
933
|
+
result[i] = sliceByColumn(stripped, 0, width, true) + (hadMarker ? CURSOR_MARKER : "");
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
901
937
|
return result;
|
|
902
938
|
}
|
|
903
939
|
|
package/src/tui.ts
CHANGED
|
@@ -998,9 +998,14 @@ export class TUI extends Container {
|
|
|
998
998
|
|
|
999
999
|
// Clamp any oversized lines before dirty-checking so the truncated form
|
|
1000
1000
|
// matches what we store in #previousLines, preventing perpetual repaints.
|
|
1001
|
+
// Strip CURSOR_MARKER before measuring — visibleWidth miscounts APC sequences
|
|
1002
|
+
// as visible chars; the marker is extracted later in #extractCursorPosition.
|
|
1001
1003
|
for (let i = 0; i < newLines.length; i++) {
|
|
1002
|
-
if (
|
|
1003
|
-
|
|
1004
|
+
if (TERMINAL.isImageLine(newLines[i])) continue;
|
|
1005
|
+
const stripped = newLines[i].replaceAll(CURSOR_MARKER, "");
|
|
1006
|
+
if (visibleWidth(stripped) > width) {
|
|
1007
|
+
const hadMarker = newLines[i] !== stripped;
|
|
1008
|
+
newLines[i] = sliceByColumn(stripped, 0, width, true) + (hadMarker ? CURSOR_MARKER : "");
|
|
1004
1009
|
}
|
|
1005
1010
|
}
|
|
1006
1011
|
|