@oh-my-pi/pi-tui 15.6.0 → 15.7.1
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/CHANGELOG.md +6 -0
- package/dist/types/tui.d.ts +12 -0
- package/package.json +3 -3
- package/src/tui.ts +37 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.7.0] - 2026-05-31
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed slash-command autocomplete repainting when a Windows Terminal session cannot report native scrollback position; live input renders can now bypass the unknown-viewport deferral without weakening background scrollback protection. ([#1550](https://github.com/can1357/oh-my-pi/issues/1550))
|
|
10
|
+
|
|
5
11
|
## [15.6.0] - 2026-05-30
|
|
6
12
|
### Added
|
|
7
13
|
|
package/dist/types/tui.d.ts
CHANGED
|
@@ -44,6 +44,18 @@ export interface Focusable {
|
|
|
44
44
|
export interface RenderRequestOptions {
|
|
45
45
|
/** Clear terminal scrollback for intentional transcript replacement. */
|
|
46
46
|
clearScrollback?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Bypass the unknown-Windows-viewport deferral for this render so the
|
|
49
|
+
* caller's intentional live UI mutation reaches the terminal even when
|
|
50
|
+
* `Terminal#isNativeViewportAtBottom()` cannot answer.
|
|
51
|
+
*
|
|
52
|
+
* Use only for renders driven by direct user interaction (autocomplete
|
|
53
|
+
* updates, IME, etc.). Any background/offscreen transcript change that
|
|
54
|
+
* coalesces into the same frame WILL also bypass the deferral and reach
|
|
55
|
+
* native scrollback — that is the trade-off, and the reason ordinary
|
|
56
|
+
* `requestRender()` calls must continue to omit this flag.
|
|
57
|
+
*/
|
|
58
|
+
allowUnknownViewportMutation?: boolean;
|
|
47
59
|
}
|
|
48
60
|
/** Options for deferred native scrollback rebuild checkpoints. */
|
|
49
61
|
export interface NativeScrollbackRefreshOptions {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-tui",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.7.1",
|
|
5
5
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"fmt": "biome format --write ."
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@oh-my-pi/pi-natives": "15.
|
|
41
|
-
"@oh-my-pi/pi-utils": "15.
|
|
40
|
+
"@oh-my-pi/pi-natives": "15.7.1",
|
|
41
|
+
"@oh-my-pi/pi-utils": "15.7.1",
|
|
42
42
|
"lru-cache": "11.5.1",
|
|
43
43
|
"marked": "^18.0.4"
|
|
44
44
|
},
|
package/src/tui.ts
CHANGED
|
@@ -84,6 +84,18 @@ export interface Focusable {
|
|
|
84
84
|
export interface RenderRequestOptions {
|
|
85
85
|
/** Clear terminal scrollback for intentional transcript replacement. */
|
|
86
86
|
clearScrollback?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Bypass the unknown-Windows-viewport deferral for this render so the
|
|
89
|
+
* caller's intentional live UI mutation reaches the terminal even when
|
|
90
|
+
* `Terminal#isNativeViewportAtBottom()` cannot answer.
|
|
91
|
+
*
|
|
92
|
+
* Use only for renders driven by direct user interaction (autocomplete
|
|
93
|
+
* updates, IME, etc.). Any background/offscreen transcript change that
|
|
94
|
+
* coalesces into the same frame WILL also bypass the deferral and reach
|
|
95
|
+
* native scrollback — that is the trade-off, and the reason ordinary
|
|
96
|
+
* `requestRender()` calls must continue to omit this flag.
|
|
97
|
+
*/
|
|
98
|
+
allowUnknownViewportMutation?: boolean;
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
/** Options for deferred native scrollback rebuild checkpoints. */
|
|
@@ -316,6 +328,7 @@ export class TUI extends Container {
|
|
|
316
328
|
#nativeScrollbackDirty = false;
|
|
317
329
|
#fullRedrawCount = 0;
|
|
318
330
|
#clearScrollbackOnNextRender = false;
|
|
331
|
+
#allowUnknownViewportMutationOnNextRender = false;
|
|
319
332
|
#hasEverRendered = false;
|
|
320
333
|
#stopped = false;
|
|
321
334
|
|
|
@@ -670,6 +683,7 @@ export class TUI extends Container {
|
|
|
670
683
|
}
|
|
671
684
|
|
|
672
685
|
requestRender(force = false, options?: RenderRequestOptions): void {
|
|
686
|
+
this.#allowUnknownViewportMutationOnNextRender ||= options?.allowUnknownViewportMutation === true;
|
|
673
687
|
if (force) {
|
|
674
688
|
this.#prepareForcedRender(options?.clearScrollback === true);
|
|
675
689
|
this.#renderRequested = true;
|
|
@@ -1138,11 +1152,19 @@ export class TUI extends Container {
|
|
|
1138
1152
|
const prevHardwareCursorRow = this.#hardwareCursorRow;
|
|
1139
1153
|
const widthChanged = this.#previousWidth > 0 && this.#previousWidth !== width;
|
|
1140
1154
|
const heightChanged = this.#previousHeight > 0 && this.#previousHeight !== height;
|
|
1155
|
+
const allowUnknownViewportMutation = this.#allowUnknownViewportMutationOnNextRender;
|
|
1156
|
+
this.#allowUnknownViewportMutationOnNextRender = false;
|
|
1141
1157
|
|
|
1142
1158
|
// 3. Classify intent.
|
|
1143
|
-
const intent = this.#planRender(
|
|
1159
|
+
const intent = this.#planRender(
|
|
1160
|
+
lines,
|
|
1161
|
+
widthChanged,
|
|
1162
|
+
heightChanged,
|
|
1163
|
+
prevViewportTop,
|
|
1164
|
+
height,
|
|
1165
|
+
allowUnknownViewportMutation,
|
|
1166
|
+
);
|
|
1144
1167
|
this.#logRedraw(intent, lines.length, height);
|
|
1145
|
-
|
|
1146
1168
|
// 4. Execute.
|
|
1147
1169
|
switch (intent.kind) {
|
|
1148
1170
|
case "noop":
|
|
@@ -1219,6 +1241,7 @@ export class TUI extends Container {
|
|
|
1219
1241
|
heightChanged: boolean,
|
|
1220
1242
|
prevViewportTop: number,
|
|
1221
1243
|
height: number,
|
|
1244
|
+
allowUnknownViewportMutation: boolean,
|
|
1222
1245
|
): RenderIntent {
|
|
1223
1246
|
// Initial paint after start(): scrollback must keep its prior shell
|
|
1224
1247
|
// content, but the viewport must be cleared so stale rows do not bleed
|
|
@@ -1253,14 +1276,14 @@ export class TUI extends Container {
|
|
|
1253
1276
|
!isMultiplexerSession()
|
|
1254
1277
|
) {
|
|
1255
1278
|
if (widthChanged || heightChanged) {
|
|
1256
|
-
if (this.#nativeViewportIsScrolled(this.#readNativeViewportAtBottom())) {
|
|
1279
|
+
if (this.#nativeViewportIsScrolled(this.#readNativeViewportAtBottom(), allowUnknownViewportMutation)) {
|
|
1257
1280
|
this.#markNativeScrollbackDirty();
|
|
1258
1281
|
return { kind: "deferredShrink", paddedLength: this.#previousLines.length };
|
|
1259
1282
|
}
|
|
1260
1283
|
return { kind: "historyRebuild" };
|
|
1261
1284
|
}
|
|
1262
1285
|
this.#markNativeScrollbackDirty();
|
|
1263
|
-
if (this.#nativeViewportIsScrolled(this.#readNativeViewportAtBottom())) {
|
|
1286
|
+
if (this.#nativeViewportIsScrolled(this.#readNativeViewportAtBottom(), allowUnknownViewportMutation)) {
|
|
1264
1287
|
return { kind: "deferredShrink", paddedLength: this.#previousLines.length };
|
|
1265
1288
|
}
|
|
1266
1289
|
return { kind: "viewportRepaint" };
|
|
@@ -1292,7 +1315,7 @@ export class TUI extends Container {
|
|
|
1292
1315
|
// through to the diff path so the append handler scrolls them into history.
|
|
1293
1316
|
if (widthChanged) {
|
|
1294
1317
|
if (diff.firstChanged < prevViewportTop) {
|
|
1295
|
-
if (this.#nativeViewportIsScrolled(this.#readNativeViewportAtBottom())) {
|
|
1318
|
+
if (this.#nativeViewportIsScrolled(this.#readNativeViewportAtBottom(), allowUnknownViewportMutation)) {
|
|
1296
1319
|
this.#markNativeScrollbackDirty();
|
|
1297
1320
|
return { kind: "viewportRepaint" };
|
|
1298
1321
|
}
|
|
@@ -1307,7 +1330,7 @@ export class TUI extends Container {
|
|
|
1307
1330
|
const structuralMutation = newLines.length !== this.#previousLines.length || diff.firstChanged < prevViewportTop;
|
|
1308
1331
|
if (!pureAppend && structuralMutation && !isMultiplexerSession()) {
|
|
1309
1332
|
const nativeViewportAtBottom = this.#readNativeViewportAtBottom();
|
|
1310
|
-
if (this.#nativeViewportIsScrolled(nativeViewportAtBottom)) {
|
|
1333
|
+
if (this.#nativeViewportIsScrolled(nativeViewportAtBottom, allowUnknownViewportMutation)) {
|
|
1311
1334
|
this.#markNativeScrollbackDirty();
|
|
1312
1335
|
return { kind: "deferredMutation" };
|
|
1313
1336
|
}
|
|
@@ -1430,8 +1453,14 @@ export class TUI extends Container {
|
|
|
1430
1453
|
return this.terminal.isNativeViewportAtBottom?.();
|
|
1431
1454
|
}
|
|
1432
1455
|
|
|
1433
|
-
#nativeViewportIsScrolled(
|
|
1434
|
-
|
|
1456
|
+
#nativeViewportIsScrolled(
|
|
1457
|
+
nativeViewportAtBottom: boolean | undefined,
|
|
1458
|
+
allowUnknownViewportMutation = false,
|
|
1459
|
+
): boolean {
|
|
1460
|
+
return (
|
|
1461
|
+
nativeViewportAtBottom === false ||
|
|
1462
|
+
(nativeViewportAtBottom === undefined && process.platform === "win32" && !allowUnknownViewportMutation)
|
|
1463
|
+
);
|
|
1435
1464
|
}
|
|
1436
1465
|
|
|
1437
1466
|
#nativeViewportIsAtBottom(nativeViewportAtBottom: boolean | undefined): boolean {
|