@oh-my-pi/pi-tui 15.7.3 → 15.7.5
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/terminal.d.ts +24 -0
- package/package.json +3 -3
- package/src/terminal.ts +31 -1
- package/src/tui.ts +35 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.7.5] - 2026-06-01
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed native Windows + Windows Terminal scrollback being yanked to the top when a streaming response triggered a TUI full redraw. Under ConPTY the `kernel32` `GetConsoleScreenBufferInfo` probe answers about the pseudo-console (always at the buffer tail) and not about WT's host scrollback, so `isNativeViewportAtBottom()` falsely returned `true` while the user was scrolled up and the shrink-across-viewport branch issued a destructive `historyRebuild` (`\x1b[2J\x1b[H\x1b[3J`). The probe now short-circuits to `undefined` whenever `WT_SESSION` is set, letting the existing deferred-rebuild path keep streaming-time mutations non-destructive and reconcile native history at the next prompt-submit checkpoint. ([#1635](https://github.com/can1357/oh-my-pi/issues/1635))
|
|
10
|
+
|
|
5
11
|
## [15.7.3] - 2026-05-31
|
|
6
12
|
|
|
7
13
|
### Added
|
package/dist/types/terminal.d.ts
CHANGED
|
@@ -41,6 +41,21 @@ export interface Terminal {
|
|
|
41
41
|
/** The last detected terminal appearance, or undefined if not yet known. */
|
|
42
42
|
get appearance(): TerminalAppearance | undefined;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Whether the native console viewport-position probe should be consulted.
|
|
46
|
+
*
|
|
47
|
+
* Returns `true` only on native Windows that is *not* fronted by Windows
|
|
48
|
+
* Terminal. The kernel32 `GetConsoleScreenBufferInfo` API answers about the
|
|
49
|
+
* ConPTY pseudo-console — which is always pinned to its tail — and not about
|
|
50
|
+
* the user-visible scrollback in modern hosts. Treat any such host as
|
|
51
|
+
* unreportable so the renderer falls back to the deferred-rebuild path.
|
|
52
|
+
*
|
|
53
|
+
* Pure helper for unit testing; the runtime call site reads `$env` /
|
|
54
|
+
* `process.platform`. See #1635.
|
|
55
|
+
*/
|
|
56
|
+
export declare function shouldTrustNativeViewportProbe(env?: {
|
|
57
|
+
WT_SESSION?: string | undefined;
|
|
58
|
+
}, platform?: NodeJS.Platform): boolean;
|
|
44
59
|
/**
|
|
45
60
|
* Real terminal using process.stdin/stdout
|
|
46
61
|
*/
|
|
@@ -53,6 +68,15 @@ export declare class ProcessTerminal implements Terminal {
|
|
|
53
68
|
/**
|
|
54
69
|
* Returns true when Windows' active console viewport is at the scrollback tail.
|
|
55
70
|
* POSIX terminals do not expose native scrollback position through a standard API.
|
|
71
|
+
*
|
|
72
|
+
* On native Windows running under Windows Terminal (the default modern
|
|
73
|
+
* host), the `kernel32` probe answers about the ConPTY pseudo-console — not
|
|
74
|
+
* the user-visible WT viewport — so it would always read "at bottom" while
|
|
75
|
+
* the user is scrolled up. Return `undefined` there so the renderer falls
|
|
76
|
+
* back to the POSIX-style deferred-rebuild path: streaming mutations stay
|
|
77
|
+
* non-destructive (no `\x1b[3J`), and the rebuild fires at the next prompt
|
|
78
|
+
* checkpoint via {@link TUI.refreshNativeScrollbackIfDirty} where the user
|
|
79
|
+
* is already pinned to the bottom by the editor keystroke. See #1635.
|
|
56
80
|
*/
|
|
57
81
|
isNativeViewportAtBottom(): boolean | undefined;
|
|
58
82
|
drainInput(maxMs?: number, idleMs?: number): Promise<void>;
|
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.7.
|
|
4
|
+
"version": "15.7.5",
|
|
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.7.
|
|
41
|
-
"@oh-my-pi/pi-utils": "15.7.
|
|
40
|
+
"@oh-my-pi/pi-natives": "15.7.5",
|
|
41
|
+
"@oh-my-pi/pi-utils": "15.7.5",
|
|
42
42
|
"lru-cache": "11.5.1",
|
|
43
43
|
"marked": "^18.0.4"
|
|
44
44
|
},
|
package/src/terminal.ts
CHANGED
|
@@ -113,6 +113,27 @@ function isWindowsSubsystemForLinux(): boolean {
|
|
|
113
113
|
return process.platform === "linux" && (!!$env.WSL_DISTRO_NAME || !!$env.WSL_INTEROP);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Whether the native console viewport-position probe should be consulted.
|
|
118
|
+
*
|
|
119
|
+
* Returns `true` only on native Windows that is *not* fronted by Windows
|
|
120
|
+
* Terminal. The kernel32 `GetConsoleScreenBufferInfo` API answers about the
|
|
121
|
+
* ConPTY pseudo-console — which is always pinned to its tail — and not about
|
|
122
|
+
* the user-visible scrollback in modern hosts. Treat any such host as
|
|
123
|
+
* unreportable so the renderer falls back to the deferred-rebuild path.
|
|
124
|
+
*
|
|
125
|
+
* Pure helper for unit testing; the runtime call site reads `$env` /
|
|
126
|
+
* `process.platform`. See #1635.
|
|
127
|
+
*/
|
|
128
|
+
export function shouldTrustNativeViewportProbe(
|
|
129
|
+
env: { WT_SESSION?: string | undefined } = $env,
|
|
130
|
+
platform: NodeJS.Platform = process.platform,
|
|
131
|
+
): boolean {
|
|
132
|
+
if (platform !== "win32") return false;
|
|
133
|
+
if (env.WT_SESSION) return false;
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
116
137
|
/**
|
|
117
138
|
* Real terminal using process.stdin/stdout
|
|
118
139
|
*/
|
|
@@ -214,9 +235,18 @@ export class ProcessTerminal implements Terminal {
|
|
|
214
235
|
/**
|
|
215
236
|
* Returns true when Windows' active console viewport is at the scrollback tail.
|
|
216
237
|
* POSIX terminals do not expose native scrollback position through a standard API.
|
|
238
|
+
*
|
|
239
|
+
* On native Windows running under Windows Terminal (the default modern
|
|
240
|
+
* host), the `kernel32` probe answers about the ConPTY pseudo-console — not
|
|
241
|
+
* the user-visible WT viewport — so it would always read "at bottom" while
|
|
242
|
+
* the user is scrolled up. Return `undefined` there so the renderer falls
|
|
243
|
+
* back to the POSIX-style deferred-rebuild path: streaming mutations stay
|
|
244
|
+
* non-destructive (no `\x1b[3J`), and the rebuild fires at the next prompt
|
|
245
|
+
* checkpoint via {@link TUI.refreshNativeScrollbackIfDirty} where the user
|
|
246
|
+
* is already pinned to the bottom by the editor keystroke. See #1635.
|
|
217
247
|
*/
|
|
218
248
|
isNativeViewportAtBottom(): boolean | undefined {
|
|
219
|
-
if (
|
|
249
|
+
if (!shouldTrustNativeViewportProbe()) return undefined;
|
|
220
250
|
try {
|
|
221
251
|
const kernel32 = dlopen("kernel32.dll", {
|
|
222
252
|
GetStdHandle: { args: [FFIType.i32], returns: FFIType.ptr },
|
package/src/tui.ts
CHANGED
|
@@ -1335,8 +1335,42 @@ export class TUI extends Container {
|
|
|
1335
1335
|
) {
|
|
1336
1336
|
return { kind: "historyRebuild" };
|
|
1337
1337
|
}
|
|
1338
|
+
// POSIX terminals — and Windows Terminal/ConPTY — that cannot report the
|
|
1339
|
+
// viewport position fall through here (`canRebuildNativeScrollbackLive` is
|
|
1340
|
+
// false). A destructive rebuild emits `\x1b[3J`, which on modern terminals
|
|
1341
|
+
// resets the viewport to the top of scrollback and yanks a scrolled-up
|
|
1342
|
+
// reader (issue #1635), so it is unsafe while the probe is unavailable.
|
|
1343
|
+
//
|
|
1344
|
+
// When the shrunk transcript now fits entirely in the viewport there is no
|
|
1345
|
+
// new native history to preserve during the live frame: repaint the screen
|
|
1346
|
+
// in place (no `\x1b[3J`) and defer stale-scrollback cleanup to the next
|
|
1347
|
+
// checkpoint rebuild (e.g. prompt submit -> `refreshNativeScrollbackIfDirty`).
|
|
1348
|
+
if (nativeViewportAtBottom === undefined && newLines.length <= height) {
|
|
1349
|
+
this.#markNativeScrollbackDirty();
|
|
1350
|
+
return { kind: "viewportRepaint" };
|
|
1351
|
+
}
|
|
1352
|
+
// The shrunk transcript still overflows the viewport. A plain viewport
|
|
1353
|
+
// repaint would re-emit the rows between the new and old viewport tops on top
|
|
1354
|
+
// of the copies the terminal already kept in native scrollback; `deferredShrink`
|
|
1355
|
+
// pads to the previous row count so no committed row is re-emitted, and the
|
|
1356
|
+
// next checkpoint rebuild cleans up.
|
|
1357
|
+
//
|
|
1358
|
+
// That deferral only carries real content when `newLines.length` reaches the
|
|
1359
|
+
// padded viewport top (`previousLines.length - height`) — otherwise every row
|
|
1360
|
+
// the padded repaint draws is past the end of `newLines` and renders blank,
|
|
1361
|
+
// hiding the prompt until the next checkpoint. This can happen even when
|
|
1362
|
+
// `scrollbackHighWater` is far below `previousLines.length - height`, because
|
|
1363
|
+
// prior unknown-POSIX viewport repaints commit longer logical frames without
|
|
1364
|
+
// moving the native scrollback boundary. For a shrink that large a blank,
|
|
1365
|
+
// uninteractable viewport is the greater evil, so yank with `historyRebuild`.
|
|
1366
|
+
// Real win32 unknown probes defer as scrolled above and never reach this; the
|
|
1367
|
+
// yank only lands on non-win32 hosts whose probe is genuinely unavailable.
|
|
1368
|
+
const paddedViewportTop = Math.max(0, this.#previousLines.length - height);
|
|
1369
|
+
if (newLines.length <= paddedViewportTop) {
|
|
1370
|
+
return { kind: "historyRebuild" };
|
|
1371
|
+
}
|
|
1338
1372
|
this.#markNativeScrollbackDirty();
|
|
1339
|
-
return { kind: "
|
|
1373
|
+
return { kind: "deferredShrink", paddedLength: this.#previousLines.length };
|
|
1340
1374
|
}
|
|
1341
1375
|
|
|
1342
1376
|
const suppressSuffixScroll = this.#suppressNextSuffixScroll;
|