@oh-my-pi/pi-tui 17.1.3 → 17.1.4
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 +7 -0
- package/package.json +3 -3
- package/src/tui.ts +47 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.1.4] - 2026-07-26
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Prevented inline Kitty graphics from covering full-width overlays such as `/switch`.
|
|
10
|
+
- Fixed Ctrl+O (expand tools) truncating the session on ConPTY hosts (native Windows and WSL): the full-view replay routed through the ConPTY frame-truncation intended only for bulk transcript-replacement paints (issue #2115), dropping every row above the retained tail behind an "older lines hidden" marker. The bound now keys on paint intent — bulk replacements (initial resume, `/resume`, handoff, resize geometry rebuilds) stay bounded, while a user-driven `resetDisplay()` (Ctrl+O expand, thinking/setting toggles, display reset) replays the whole transcript ([#4863](https://github.com/can1357/oh-my-pi/issues/4863)).
|
|
11
|
+
|
|
5
12
|
## [17.1.2] - 2026-07-24
|
|
6
13
|
|
|
7
14
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-tui",
|
|
4
|
-
"version": "17.1.
|
|
4
|
+
"version": "17.1.4",
|
|
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": "17.1.
|
|
41
|
-
"@oh-my-pi/pi-utils": "17.1.
|
|
40
|
+
"@oh-my-pi/pi-natives": "17.1.4",
|
|
41
|
+
"@oh-my-pi/pi-utils": "17.1.4",
|
|
42
42
|
"lru-cache": "11.5.2",
|
|
43
43
|
"marked": "^18.0.6"
|
|
44
44
|
},
|
package/src/tui.ts
CHANGED
|
@@ -1055,6 +1055,13 @@ export class TUI extends Container {
|
|
|
1055
1055
|
#ghosttyInitialImageDelayTimer: RenderTimer | undefined;
|
|
1056
1056
|
#ghosttyImageReadyAtMs = 0;
|
|
1057
1057
|
#clearScrollbackOnNextRender = false;
|
|
1058
|
+
// Set by `resetDisplay()` and consumed by the next authoritative normal-screen
|
|
1059
|
+
// render. If that render is a full paint, it is a user-driven replay of the
|
|
1060
|
+
// current transcript (Ctrl+O expand, thinking/setting toggles, display reset)
|
|
1061
|
+
// that must show every row, so it opts out of #truncateLargeConptyFrame.
|
|
1062
|
+
// Multiplexer resets render as in-place updates; consuming the flag there
|
|
1063
|
+
// prevents a later /resume or handoff bulk replacement from inheriting it.
|
|
1064
|
+
#unboundedConptyPaintRequested = false;
|
|
1058
1065
|
#forceViewportRepaintOnNextRender = false;
|
|
1059
1066
|
#hasEverRendered = false;
|
|
1060
1067
|
#scrollbackRebuildEnabled =
|
|
@@ -1889,6 +1896,10 @@ export class TUI extends Container {
|
|
|
1889
1896
|
*/
|
|
1890
1897
|
resetDisplay(): void {
|
|
1891
1898
|
if (this.#stopped) return;
|
|
1899
|
+
// This is a user-driven redraw of the current transcript; it must replay
|
|
1900
|
+
// every row, so opt the next full paint out of the ConPTY resume bound.
|
|
1901
|
+
// Set before the multiplexer early-return so it survives a deferred paint.
|
|
1902
|
+
this.#unboundedConptyPaintRequested = true;
|
|
1892
1903
|
this.invalidate();
|
|
1893
1904
|
// A reset that lands inside a tmux/screen/zellij resize burst would
|
|
1894
1905
|
// paint mid-reflow and re-introduce the flash race (issue #2088).
|
|
@@ -2655,7 +2666,14 @@ export class TUI extends Container {
|
|
|
2655
2666
|
overlayWidth: number,
|
|
2656
2667
|
totalWidth: number,
|
|
2657
2668
|
): string {
|
|
2658
|
-
if (TERMINAL.isImageLine(baseLine))
|
|
2669
|
+
if (TERMINAL.isImageLine(baseLine)) {
|
|
2670
|
+
// Full-width overlays such as /switch are opaque: replace the
|
|
2671
|
+
// Unicode placeholder cells so the image cannot cover the modal.
|
|
2672
|
+
// Partial overlays cannot safely splice placement control sequences.
|
|
2673
|
+
if (startCol !== 0 || overlayWidth < totalWidth) return baseLine;
|
|
2674
|
+
const overlay = sliceWithWidth(overlayLine, 0, totalWidth, true);
|
|
2675
|
+
return SEGMENT_RESET + overlay.text + " ".repeat(Math.max(0, totalWidth - overlay.width));
|
|
2676
|
+
}
|
|
2659
2677
|
|
|
2660
2678
|
// Single pass through baseLine extracts both before and after segments
|
|
2661
2679
|
const afterStart = startCol + overlayWidth;
|
|
@@ -3154,6 +3172,12 @@ export class TUI extends Container {
|
|
|
3154
3172
|
}
|
|
3155
3173
|
const cursorTrackingLineCount = hasVisibleOverlay ? Math.max(frame.length, windowTop + height) : frame.length;
|
|
3156
3174
|
|
|
3175
|
+
// `resetDisplay()` requests an unbounded replay of the current
|
|
3176
|
+
// transcript. Consume that one-shot intent on this authoritative
|
|
3177
|
+
// normal-screen render even when a multiplexer makes it an in-place
|
|
3178
|
+
// update; otherwise a later /resume or handoff full paint inherits it.
|
|
3179
|
+
const unboundedConptyPaint = this.#unboundedConptyPaintRequested;
|
|
3180
|
+
this.#unboundedConptyPaintRequested = false;
|
|
3157
3181
|
const intent: RenderIntent = fullPaint
|
|
3158
3182
|
? {
|
|
3159
3183
|
kind: "fullPaint",
|
|
@@ -3187,6 +3211,7 @@ export class TUI extends Container {
|
|
|
3187
3211
|
chunkTo,
|
|
3188
3212
|
windowTop,
|
|
3189
3213
|
cursorTrackingLineCount,
|
|
3214
|
+
boundConptyPaint: !unboundedConptyPaint,
|
|
3190
3215
|
leadingSequence: deferredAltExit,
|
|
3191
3216
|
});
|
|
3192
3217
|
this.#pendingAltExit = "";
|
|
@@ -3588,6 +3613,16 @@ export class TUI extends Container {
|
|
|
3588
3613
|
chunkTo: number;
|
|
3589
3614
|
windowTop: number;
|
|
3590
3615
|
cursorTrackingLineCount: number;
|
|
3616
|
+
/**
|
|
3617
|
+
* Whether this paint may be bounded by {@link #truncateLargeConptyFrame}
|
|
3618
|
+
* on ConPTY hosts. True for bulk transcript-replacement paints — first
|
|
3619
|
+
* paint, /resume, handoff, and resize geometry rebuilds — where a
|
|
3620
|
+
* multi-megabyte synchronized frame stalls conhost (issue #2115). False
|
|
3621
|
+
* for a user-driven `resetDisplay()` (Ctrl+O expand, thinking/setting
|
|
3622
|
+
* toggles, display reset), which must replay the whole transcript so
|
|
3623
|
+
* nothing is silently dropped from scrollback (issue #4863).
|
|
3624
|
+
*/
|
|
3625
|
+
boundConptyPaint: boolean;
|
|
3591
3626
|
leadingSequence: string;
|
|
3592
3627
|
},
|
|
3593
3628
|
): void {
|
|
@@ -3604,16 +3639,19 @@ export class TUI extends Container {
|
|
|
3604
3639
|
paintCursorPos = { row: chunkTo + cursorPos.row - windowTop, col: cursorPos.col };
|
|
3605
3640
|
}
|
|
3606
3641
|
}
|
|
3607
|
-
// ConPTY hosts bound
|
|
3608
|
-
//
|
|
3609
|
-
//
|
|
3610
|
-
//
|
|
3611
|
-
//
|
|
3612
|
-
//
|
|
3613
|
-
//
|
|
3642
|
+
// ConPTY hosts bound bulk transcript-replacement replays (resume, handoff,
|
|
3643
|
+
// first paint, resize): merge prefix + window into one array so
|
|
3644
|
+
// #truncateLargeConptyFrame can measure the payload and retain only the
|
|
3645
|
+
// tail (#2115). Gated on `boundConptyPaint` — a user-driven `resetDisplay()`
|
|
3646
|
+
// (Ctrl+O expand, toggles) sets it false and replays the whole transcript
|
|
3647
|
+
// untruncated so nothing is dropped from scrollback (#4863). Gated on the
|
|
3648
|
+
// host check too — everywhere else the merge would copy a pointer per
|
|
3649
|
+
// committed row (a 50k-row session = 50k-entry array per resize step /
|
|
3650
|
+
// theme change / session replace) just to be returned unchanged.
|
|
3651
|
+
// `paintLines` stays null unless truncation actually rewrote the replay.
|
|
3614
3652
|
let paintLines: string[] | null = null;
|
|
3615
3653
|
let paintLineCount = chunkTo + height;
|
|
3616
|
-
if (isConPTYHosted()) {
|
|
3654
|
+
if (options.boundConptyPaint && isConPTYHosted()) {
|
|
3617
3655
|
const merged = new Array<string>(chunkTo + height);
|
|
3618
3656
|
for (let i = 0; i < chunkTo; i++) merged[i] = frame[i] ?? "";
|
|
3619
3657
|
for (let screenRow = 0; screenRow < height; screenRow++) {
|