@oh-my-pi/pi-tui 17.2.3 → 17.2.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 CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.2.5] - 2026-08-03
6
+
7
+ ### Fixed
8
+
9
+ - Fixed Kitty and Ghostty keyboard shortcuts on non-Latin keyboard layouts by requesting base-layout key reporting from the terminal.
10
+
11
+ ## [17.2.4] - 2026-08-01
12
+
13
+ ### Fixed
14
+
15
+ - Fixed animated Loader paints saturating a CPU core on slow WSL/ConPTY terminals by applying cost-aware cadence backpressure while preserving 30fps on cheap frames ([#7290](https://github.com/can1357/oh-my-pi/issues/7290)).
16
+ - Fixed interactive terminals suppressing all output and input when the host project sets `NODE_ENV=test` or `BUN_ENV=test` ([#7261](https://github.com/can1357/oh-my-pi/issues/7261)).
17
+
5
18
  ## [17.2.2] - 2026-07-31
6
19
 
7
20
  ### Added
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.2.3",
4
+ "version": "17.2.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": "17.2.3",
41
- "@oh-my-pi/pi-utils": "17.2.3",
40
+ "@oh-my-pi/pi-natives": "17.2.5",
41
+ "@oh-my-pi/pi-utils": "17.2.5",
42
42
  "lru-cache": "11.5.2",
43
43
  "marked": "^18.0.6"
44
44
  },
@@ -4,6 +4,8 @@ import { Text } from "./text";
4
4
 
5
5
  const RENDER_INTERVAL_MS = 1000 / 30;
6
6
  const SPINNER_ADVANCE_MS = 80;
7
+ const MAX_RENDER_BACKPRESSURE_MS = 200;
8
+ const RENDER_BACKPRESSURE_MULTIPLIER = 9;
7
9
 
8
10
  type ColorFn = (str: string) => string;
9
11
 
@@ -98,25 +100,12 @@ export class Loader extends Text {
98
100
  this.#syncText();
99
101
  this.#requestPaint();
100
102
  const intervalMs = this.messageColorFn.animated === true ? RENDER_INTERVAL_MS : SPINNER_ADVANCE_MS;
101
- this.#intervalId = setInterval(() => {
102
- const now = performance.now();
103
- const elapsed = now - this.#lastSpinnerTick;
104
- const shouldAdvanceSpinner = elapsed >= SPINNER_ADVANCE_MS;
105
- if (shouldAdvanceSpinner) {
106
- const steps = Math.floor(elapsed / SPINNER_ADVANCE_MS);
107
- this.#currentFrame = (this.#currentFrame + steps) % this.#frames.length;
108
- this.#lastSpinnerTick += steps * SPINNER_ADVANCE_MS;
109
- this.#syncText();
110
- }
111
- if (shouldAdvanceSpinner || this.#ui?.synchronizedOutput === true) {
112
- this.#requestPaint();
113
- }
114
- }, intervalMs);
103
+ this.#scheduleTick(intervalMs, intervalMs);
115
104
  }
116
105
 
117
106
  stop() {
118
107
  if (this.#intervalId) {
119
- clearInterval(this.#intervalId);
108
+ clearTimeout(this.#intervalId);
120
109
  this.#intervalId = undefined;
121
110
  }
122
111
  }
@@ -135,6 +124,32 @@ export class Loader extends Text {
135
124
  this.#requestPaint();
136
125
  }
137
126
 
127
+ #scheduleTick(intervalMs: number, delayMs: number): void {
128
+ const timer = setTimeout(() => {
129
+ if (this.#intervalId !== timer) return;
130
+ const startedAt = performance.now();
131
+ const elapsed = startedAt - this.#lastSpinnerTick;
132
+ const shouldAdvanceSpinner = elapsed >= SPINNER_ADVANCE_MS;
133
+ if (shouldAdvanceSpinner) {
134
+ const steps = Math.floor(elapsed / SPINNER_ADVANCE_MS);
135
+ this.#currentFrame = (this.#currentFrame + steps) % this.#frames.length;
136
+ this.#lastSpinnerTick += steps * SPINNER_ADVANCE_MS;
137
+ this.#syncText();
138
+ }
139
+ if (shouldAdvanceSpinner || this.#ui?.synchronizedOutput === true) {
140
+ this.#requestPaint();
141
+ }
142
+
143
+ const frameCostMs = performance.now() - startedAt;
144
+ if (this.#intervalId !== timer) return;
145
+ const cadenceDelayMs = Math.max(0, intervalMs - frameCostMs);
146
+ // Idle for nine times the paint cost to keep animation at or below
147
+ // 10% CPU, while cheap frames retain their original cadence.
148
+ const backpressureDelayMs = Math.min(MAX_RENDER_BACKPRESSURE_MS, frameCostMs * RENDER_BACKPRESSURE_MULTIPLIER);
149
+ this.#scheduleTick(intervalMs, Math.max(cadenceDelayMs, backpressureDelayMs));
150
+ }, delayMs);
151
+ this.#intervalId = timer;
152
+ }
138
153
  /** Re-wrap the underlying Text only when its message or frame width changes. */
139
154
  #syncText(): boolean {
140
155
  const layoutFrame = this.#layoutFrames[this.#currentFrame];
package/src/terminal.ts CHANGED
@@ -404,7 +404,7 @@ export interface Terminal {
404
404
  // Whether Kitty keyboard protocol is active
405
405
  get kittyProtocolActive(): boolean;
406
406
 
407
- // The exact kitty keyboard push sequence in effect ("\x1b[>1u" or "\x1b[>7u"),
407
+ // The exact kitty keyboard push sequence in effect ("\x1b[>5u" or "\x1b[>7u"),
408
408
  // or null when the protocol is not active. Kitty keyboard flags are per-screen,
409
409
  // so the TUI re-pushes this after entering the alternate screen.
410
410
  get kittyEnableSequence(): string | null;
@@ -1100,15 +1100,15 @@ export class ProcessTerminal implements Terminal {
1100
1100
  const reportedFlags = parseInt(match[1]!, 10);
1101
1101
  this.#kittyProtocolActive = true;
1102
1102
  setKittyProtocolActive(true);
1103
- if (reportedFlags >= 3) {
1104
- // Already enriched (Ghostty/foot may keep flags from a parent app).
1105
- // Push level-2 to lock in event reporting.
1103
+ if ((reportedFlags & 2) !== 0) {
1104
+ // Preserve event-type reporting already enabled by a parent app.
1105
+ // Push level-2 to keep its shortcuts reporting consistently.
1106
1106
  this.#kittyEnableSeq = "\x1b[>7u";
1107
1107
  this.#safeWrite(this.#kittyEnableSeq);
1108
1108
  } else {
1109
- // Level 1 (disambiguate escape codes) enough for Shift+Enter
1110
- // without the modifyOtherKeys fallback that caused regression #3259.
1111
- this.#kittyEnableSeq = "\x1b[>1u";
1109
+ // Disambiguate escape codes and report base-layout keys for physical
1110
+ // shortcut matching, without event reporting that caused regression #3259.
1111
+ this.#kittyEnableSeq = "\x1b[>5u";
1112
1112
  this.#safeWrite(this.#kittyEnableSeq);
1113
1113
  }
1114
1114
  return;