@oh-my-pi/pi-tui 15.7.3 → 15.7.6
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 +16 -0
- package/dist/types/terminal.d.ts +24 -0
- package/package.json +3 -3
- package/src/autocomplete.ts +3 -1
- package/src/terminal.ts +31 -1
- package/src/tui.ts +59 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.7.6] - 2026-06-01
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed native Windows + Windows Terminal freezing the editor on the wrap keystroke, on `/plan`/`/resume`/model-switch/status-line toggles, and on any other offscreen structural mutation until the next prompt submit. The `15.7.5` `#1635` fix routed every viewport-saturating pure-append and structural mutation through `deferredMutation` (a literal no-op) whenever `isNativeViewportAtBottom()` returned `undefined` — which it always does under `WT_SESSION` because the kernel32 probe can't see WT host scrollback. The deferral was only ever meant for the *confirmed-scrolled* case; an unknown viewport now falls back to a non-destructive `viewportRepaint` instead, so the live UI keeps updating without emitting `\x1b[3J` and without yanking a possibly-scrolled reader. Confirmed-scrolled frames (probe returns `false`) still defer.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Removed the hard-coded 20-result cap on `@`-prefixed fuzzy file completion in `CombinedAutocompleteProvider.#getFuzzyFileSuggestions`. The dropdown now honors the existing `maxResults: 100` ceiling already configured for `fuzzyFind`, so projects with many files sharing a common stem (e.g. `@controller`, `@test`) surface all relevant matches instead of being silently truncated. ([#1652](https://github.com/can1357/oh-my-pi/issues/1652))
|
|
14
|
+
|
|
15
|
+
## [15.7.5] - 2026-06-01
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- 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))
|
|
20
|
+
|
|
5
21
|
## [15.7.3] - 2026-05-31
|
|
6
22
|
|
|
7
23
|
### 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.6",
|
|
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.6",
|
|
41
|
+
"@oh-my-pi/pi-utils": "15.7.6",
|
|
42
42
|
"lru-cache": "11.5.1",
|
|
43
43
|
"marked": "^18.0.4"
|
|
44
44
|
},
|
package/src/autocomplete.ts
CHANGED
|
@@ -736,7 +736,9 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
|
|
736
736
|
}
|
|
737
737
|
return lowerQuery.length === 0 || fuzzyMatch(lowerQuery, normalized.toLowerCase());
|
|
738
738
|
});
|
|
739
|
-
|
|
739
|
+
// `fuzzyFind` is already capped via `maxResults` in
|
|
740
|
+
// `buildAutocompleteFuzzyDiscoveryProfile`; no extra slice here.
|
|
741
|
+
const topEntries = filteredMatches;
|
|
740
742
|
const suggestions: AutocompleteItem[] = [];
|
|
741
743
|
for (const { path: entryPath, isDirectory } of topEntries) {
|
|
742
744
|
const pathWithoutSlash = isDirectory ? entryPath.slice(0, -1) : entryPath;
|
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;
|
|
@@ -1405,14 +1439,32 @@ export class TUI extends Container {
|
|
|
1405
1439
|
const nativeViewportAtBottom = this.#readNativeViewportAtBottom();
|
|
1406
1440
|
if (this.#nativeViewportIsScrolled(nativeViewportAtBottom, allowUnknownViewportMutation)) {
|
|
1407
1441
|
this.#markNativeScrollbackDirty();
|
|
1408
|
-
|
|
1442
|
+
// Confirmed scrolled (probe returned `false`): the reader is parked in
|
|
1443
|
+
// scrollback and writing the live frame is wasted bytes — defer until
|
|
1444
|
+
// the next checkpoint reconciles. Unknown viewport (e.g. native Windows
|
|
1445
|
+
// Terminal where the probe cannot see WT host scrollback) is a
|
|
1446
|
+
// different case: a no-op there freezes the editor on the keystroke
|
|
1447
|
+
// that grows `lines.length` past the viewport (the wrap keystroke).
|
|
1448
|
+
// Fall through to a non-destructive viewport repaint instead so the
|
|
1449
|
+
// live UI keeps updating without yanking a possibly-scrolled reader.
|
|
1450
|
+
if (this.#nativeViewportIsKnownScrolled(nativeViewportAtBottom)) {
|
|
1451
|
+
return { kind: "deferredMutation" };
|
|
1452
|
+
}
|
|
1453
|
+
return { kind: "viewportRepaint" };
|
|
1409
1454
|
}
|
|
1410
1455
|
}
|
|
1411
1456
|
if (!pureAppend && structuralMutation && !isMultiplexerSession()) {
|
|
1412
1457
|
const nativeViewportAtBottom = this.#readNativeViewportAtBottom();
|
|
1413
1458
|
if (this.#nativeViewportIsScrolled(nativeViewportAtBottom, allowUnknownViewportMutation)) {
|
|
1414
1459
|
this.#markNativeScrollbackDirty();
|
|
1415
|
-
|
|
1460
|
+
// See the matching comment on the pure-append branch above: confirmed
|
|
1461
|
+
// scrolled stays a no-op; unknown viewport repaints the visible window
|
|
1462
|
+
// so slash-command transitions and offscreen chrome edits paint on the
|
|
1463
|
+
// same frame instead of stalling until the next prompt submit.
|
|
1464
|
+
if (this.#nativeViewportIsKnownScrolled(nativeViewportAtBottom)) {
|
|
1465
|
+
return { kind: "deferredMutation" };
|
|
1466
|
+
}
|
|
1467
|
+
return { kind: "viewportRepaint" };
|
|
1416
1468
|
}
|
|
1417
1469
|
// The append-tail path can only scroll a clean pure-tail append over an
|
|
1418
1470
|
// offscreen edit into history: the rows it pushes must equal the net
|
|
@@ -1587,6 +1639,10 @@ export class TUI extends Container {
|
|
|
1587
1639
|
);
|
|
1588
1640
|
}
|
|
1589
1641
|
|
|
1642
|
+
#nativeViewportIsKnownScrolled(nativeViewportAtBottom: boolean | undefined): boolean {
|
|
1643
|
+
return nativeViewportAtBottom === false;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1590
1646
|
#nativeViewportIsAtBottom(nativeViewportAtBottom: boolean | undefined): boolean {
|
|
1591
1647
|
return nativeViewportAtBottom === true;
|
|
1592
1648
|
}
|