@oh-my-pi/pi-tui 16.3.15 → 16.4.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +3 -3
  3. package/src/tui.ts +17 -12
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.4.0] - 2026-07-10
6
+
7
+ ### Fixed
8
+
9
+ - Fixed terminal flickering during session resume, replacement, or resizing on terminals that do not support synchronized output.
10
+
5
11
  ## [16.3.14] - 2026-07-09
6
12
 
7
13
  ### 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": "16.3.15",
4
+ "version": "16.4.0",
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": "16.3.15",
41
- "@oh-my-pi/pi-utils": "16.3.15",
40
+ "@oh-my-pi/pi-natives": "16.4.0",
41
+ "@oh-my-pi/pi-utils": "16.4.0",
42
42
  "lru-cache": "11.5.1",
43
43
  "marked": "^18.0.5"
44
44
  },
package/src/tui.ts CHANGED
@@ -561,10 +561,9 @@ export class Container implements Component {
561
561
  * method owns the bytes written and the state update.
562
562
  *
563
563
  * - `fullPaint`: gesture-driven replay — initial paint, session replacement,
564
- * resize, resetDisplay. Clears the viewport and (for destructive replaces,
565
- * outside multiplexers) native scrollback via ED3, then writes the
566
- * committed prefix and the visible window. The only ED3 callsite in the
567
- * engine.
564
+ * resize, resetDisplay. Rewrites the frame from home; destructive replaces
565
+ * clear native scrollback via ED3 without first blanking the viewport. The
566
+ * only ED3 callsite in the engine.
568
567
  * - `update`: ordinary frame. Commits the newly settled chunk at the
569
568
  * scrollback seam (if any) and repaints the window with relative moves.
570
569
  */
@@ -3163,7 +3162,7 @@ export class TUI extends Container {
3163
3162
  }
3164
3163
 
3165
3164
  /**
3166
- * Clear the viewport (optionally native scrollback) and replay the frame:
3165
+ * Replay the frame from home, optionally clearing native scrollback first:
3167
3166
  * committed prefix `[0, chunkTo)` followed by the visible window. ED3
3168
3167
  * (`CSI 3 J`) is emitted here and only here, and only for gesture-driven
3169
3168
  * paints (session replace, resize, resetDisplay, or an explicit
@@ -3221,7 +3220,10 @@ export class TUI extends Container {
3221
3220
  }
3222
3221
  let buffer = this.#paintBeginSequence + this.#leaveResizeAltSequence() + purgeSequence;
3223
3222
  if (options.clearScrollback) {
3224
- buffer += "\x1b[2J\x1b[H\x1b[3J";
3223
+ // Clear native history without blanking the live viewport first. The
3224
+ // replay below rewrites every visible row from home, including blanks,
3225
+ // so terminals without DEC 2026 never expose an ED2-cleared frame.
3226
+ buffer += "\x1b[H\x1b[3J";
3225
3227
  } else {
3226
3228
  // Best-effort: push the pre-paint screen into scrollback on
3227
3229
  // terminals that implement kitty's ED 22
@@ -3254,21 +3256,24 @@ export class TUI extends Container {
3254
3256
  if (paintLines === null) {
3255
3257
  // Common path: emit straight from the source arrays (the
3256
3258
  // pre-merge two-loop form); byte-identical to replaying the
3257
- // merged array.
3259
+ // merged array. Destructive history clears deliberately avoid ED2, so
3260
+ // each row must self-clear stale cells left by the previous viewport.
3258
3261
  for (let i = 0; i < chunkTo; i++) {
3259
3262
  if (i > 0) buffer += "\r\n";
3260
- buffer += this.#terminalLine(frame[i] ?? "");
3263
+ buffer += options.clearScrollback
3264
+ ? this.#lineRewriteSequence(frame[i] ?? "", width)
3265
+ : this.#terminalLine(frame[i] ?? "");
3261
3266
  }
3262
3267
  for (let screenRow = 0; screenRow < height; screenRow++) {
3263
3268
  if (chunkTo + screenRow > 0) buffer += "\r\n";
3264
- buffer += this.#terminalLine(visibleTexts ? (visibleTexts[screenRow] ?? "") : (window[screenRow] ?? ""));
3269
+ const line = visibleTexts ? (visibleTexts[screenRow] ?? "") : (window[screenRow] ?? "");
3270
+ buffer += options.clearScrollback ? this.#lineRewriteSequence(line, width) : this.#terminalLine(line);
3265
3271
  }
3266
3272
  } else {
3267
3273
  for (let i = 0; i < paintLines.length; i++) {
3268
3274
  if (i > 0) buffer += "\r\n";
3269
- buffer += this.#terminalLine(
3270
- visibleTexts && i >= visibleStart ? visibleTexts[i - visibleStart] : (paintLines[i] ?? ""),
3271
- );
3275
+ const line = visibleTexts && i >= visibleStart ? visibleTexts[i - visibleStart] : (paintLines[i] ?? "");
3276
+ buffer += options.clearScrollback ? this.#lineRewriteSequence(line, width) : this.#terminalLine(line);
3272
3277
  }
3273
3278
  }
3274
3279
  buffer += fillSequence;