@oh-my-pi/pi-tui 12.10.0 → 12.11.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/tui.ts +40 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-tui",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.11.0",
|
|
4
4
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"bun": ">=1.3.7"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@oh-my-pi/pi-natives": "12.
|
|
56
|
-
"@oh-my-pi/pi-utils": "12.
|
|
55
|
+
"@oh-my-pi/pi-natives": "12.11.0",
|
|
56
|
+
"@oh-my-pi/pi-utils": "12.11.0",
|
|
57
57
|
"@types/mime-types": "^3.0.1",
|
|
58
58
|
"chalk": "^5.6.2",
|
|
59
59
|
"marked": "^17.0.2",
|
package/src/tui.ts
CHANGED
|
@@ -885,10 +885,13 @@ export class TUI extends Container {
|
|
|
885
885
|
if (i > 0) buffer += "\r\n";
|
|
886
886
|
buffer += newLines[i];
|
|
887
887
|
}
|
|
888
|
+
const renderCursorRow = Math.max(0, newLines.length - 1);
|
|
889
|
+
const cursorUpdate = this.#buildHardwareCursorSequence(cursorPos, newLines.length, renderCursorRow);
|
|
890
|
+
buffer += cursorUpdate.sequence;
|
|
888
891
|
buffer += "\x1b[?2026l"; // End synchronized output
|
|
889
892
|
this.terminal.write(buffer);
|
|
890
|
-
this.#cursorRow =
|
|
891
|
-
this.#hardwareCursorRow =
|
|
893
|
+
this.#cursorRow = renderCursorRow;
|
|
894
|
+
this.#hardwareCursorRow = cursorUpdate.row;
|
|
892
895
|
// Reset max lines when clearing, otherwise track growth
|
|
893
896
|
if (clear) {
|
|
894
897
|
this.#maxLinesRendered = newLines.length;
|
|
@@ -896,7 +899,6 @@ export class TUI extends Container {
|
|
|
896
899
|
this.#maxLinesRendered = Math.max(this.#maxLinesRendered, newLines.length);
|
|
897
900
|
}
|
|
898
901
|
this.#previousViewportTop = Math.max(0, this.#maxLinesRendered - height);
|
|
899
|
-
this.#positionHardwareCursor(cursorPos, newLines.length);
|
|
900
902
|
this.#previousLines = newLines;
|
|
901
903
|
this.#previousWidth = width;
|
|
902
904
|
};
|
|
@@ -965,10 +967,9 @@ export class TUI extends Container {
|
|
|
965
967
|
|
|
966
968
|
// All changes are in deleted lines (nothing to render, just clear)
|
|
967
969
|
if (firstChanged >= newLines.length) {
|
|
970
|
+
const targetRow = Math.max(0, newLines.length - 1);
|
|
968
971
|
if (this.#previousLines.length > newLines.length) {
|
|
969
972
|
let buffer = "\x1b[?2026h";
|
|
970
|
-
// Move to end of new content (clamp to 0 for empty content)
|
|
971
|
-
const targetRow = Math.max(0, newLines.length - 1);
|
|
972
973
|
const lineDiff = computeLineDiff(targetRow);
|
|
973
974
|
if (lineDiff > 0) buffer += `\x1b[${lineDiff}B`;
|
|
974
975
|
else if (lineDiff < 0) buffer += `\x1b[${-lineDiff}A`;
|
|
@@ -990,12 +991,15 @@ export class TUI extends Container {
|
|
|
990
991
|
if (extraLines > 0) {
|
|
991
992
|
buffer += `\x1b[${extraLines}A`;
|
|
992
993
|
}
|
|
994
|
+
const cursorUpdate = this.#buildHardwareCursorSequence(cursorPos, newLines.length, targetRow);
|
|
995
|
+
buffer += cursorUpdate.sequence;
|
|
993
996
|
buffer += "\x1b[?2026l";
|
|
994
997
|
this.terminal.write(buffer);
|
|
995
|
-
this.#
|
|
996
|
-
|
|
998
|
+
this.#hardwareCursorRow = cursorUpdate.row;
|
|
999
|
+
} else {
|
|
1000
|
+
this.#positionHardwareCursor(cursorPos, newLines.length);
|
|
997
1001
|
}
|
|
998
|
-
this.#
|
|
1002
|
+
this.#cursorRow = targetRow;
|
|
999
1003
|
this.#previousLines = newLines;
|
|
1000
1004
|
this.#previousWidth = width;
|
|
1001
1005
|
this.#previousViewportTop = Math.max(0, this.#maxLinesRendered - height);
|
|
@@ -1098,8 +1102,9 @@ export class TUI extends Container {
|
|
|
1098
1102
|
buffer += `\x1b[${extraLines}A`;
|
|
1099
1103
|
}
|
|
1100
1104
|
|
|
1105
|
+
const cursorUpdate = this.#buildHardwareCursorSequence(cursorPos, newLines.length, finalCursorRow);
|
|
1106
|
+
buffer += cursorUpdate.sequence;
|
|
1101
1107
|
buffer += "\x1b[?2026l"; // End synchronized output
|
|
1102
|
-
|
|
1103
1108
|
if (process.env.PI_TUI_DEBUG === "1") {
|
|
1104
1109
|
const debugDir = "/tmp/tui";
|
|
1105
1110
|
fs.mkdirSync(debugDir, { recursive: true });
|
|
@@ -1128,61 +1133,55 @@ export class TUI extends Container {
|
|
|
1128
1133
|
].join("\n");
|
|
1129
1134
|
fs.writeFileSync(debugPath, debugData);
|
|
1130
1135
|
}
|
|
1131
|
-
|
|
1132
1136
|
// Write entire buffer at once
|
|
1133
1137
|
this.terminal.write(buffer);
|
|
1134
|
-
|
|
1135
|
-
// Track cursor position for next render
|
|
1136
1138
|
// cursorRow tracks end of content (for viewport calculation)
|
|
1137
1139
|
// hardwareCursorRow tracks actual terminal cursor position (for movement)
|
|
1138
1140
|
this.#cursorRow = Math.max(0, newLines.length - 1);
|
|
1139
|
-
this.#hardwareCursorRow =
|
|
1141
|
+
this.#hardwareCursorRow = cursorUpdate.row;
|
|
1140
1142
|
// Track terminal's working area (grows but doesn't shrink unless cleared)
|
|
1141
1143
|
this.#maxLinesRendered = Math.max(this.#maxLinesRendered, newLines.length);
|
|
1142
1144
|
this.#previousViewportTop = Math.max(0, this.#maxLinesRendered - height);
|
|
1143
|
-
|
|
1144
|
-
// Position hardware cursor for IME
|
|
1145
|
-
this.#positionHardwareCursor(cursorPos, newLines.length);
|
|
1146
|
-
|
|
1147
1145
|
this.#previousLines = newLines;
|
|
1148
1146
|
this.#previousWidth = width;
|
|
1149
1147
|
}
|
|
1150
1148
|
|
|
1151
1149
|
/**
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1154
|
-
* @param totalLines Total number of rendered lines
|
|
1150
|
+
* Build cursor movement and visibility escape sequence and return resulting row.
|
|
1151
|
+
* Used by differential rendering so cursor placement stays inside synchronized output.
|
|
1155
1152
|
*/
|
|
1156
|
-
#
|
|
1153
|
+
#buildHardwareCursorSequence(
|
|
1154
|
+
cursorPos: { row: number; col: number } | null,
|
|
1155
|
+
totalLines: number,
|
|
1156
|
+
currentRow: number,
|
|
1157
|
+
): { sequence: string; row: number } {
|
|
1157
1158
|
if (!cursorPos || totalLines <= 0) {
|
|
1158
|
-
|
|
1159
|
-
return;
|
|
1159
|
+
return { sequence: "\x1b[?25l", row: currentRow };
|
|
1160
1160
|
}
|
|
1161
|
-
|
|
1162
1161
|
// Clamp cursor position to valid range
|
|
1163
1162
|
const targetRow = Math.max(0, Math.min(cursorPos.row, totalLines - 1));
|
|
1164
1163
|
const targetCol = Math.max(0, cursorPos.col);
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
const rowDelta = targetRow - this.#hardwareCursorRow;
|
|
1168
|
-
let buffer = "";
|
|
1164
|
+
let sequence = "";
|
|
1165
|
+
const rowDelta = targetRow - currentRow;
|
|
1169
1166
|
if (rowDelta > 0) {
|
|
1170
|
-
|
|
1167
|
+
sequence += `\x1b[${rowDelta}B`; // Move down
|
|
1171
1168
|
} else if (rowDelta < 0) {
|
|
1172
|
-
|
|
1169
|
+
sequence += `\x1b[${-rowDelta}A`; // Move up
|
|
1173
1170
|
}
|
|
1174
|
-
// Move to absolute column (1-indexed)
|
|
1175
|
-
|
|
1171
|
+
sequence += `\x1b[${targetCol + 1}G`; // Move to absolute column (1-indexed)
|
|
1172
|
+
sequence += this.#showHardwareCursor ? "\x1b[?25h" : "\x1b[?25l";
|
|
1176
1173
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
}
|
|
1174
|
+
return { sequence, row: targetRow };
|
|
1175
|
+
}
|
|
1180
1176
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1177
|
+
/**
|
|
1178
|
+
* Position the hardware cursor for IME candidate window.
|
|
1179
|
+
* @param cursorPos The cursor position extracted from rendered output, or null
|
|
1180
|
+
* @param totalLines Total number of rendered lines
|
|
1181
|
+
*/
|
|
1182
|
+
#positionHardwareCursor(cursorPos: { row: number; col: number } | null, totalLines: number): void {
|
|
1183
|
+
const update = this.#buildHardwareCursorSequence(cursorPos, totalLines, this.#hardwareCursorRow);
|
|
1184
|
+
this.terminal.write(update.sequence);
|
|
1185
|
+
this.#hardwareCursorRow = update.row;
|
|
1187
1186
|
}
|
|
1188
1187
|
}
|