@oh-my-pi/pi-tui 16.1.19 → 16.1.21
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/dist/types/terminal-capabilities.d.ts +4 -2
- package/package.json +3 -3
- package/src/terminal-capabilities.ts +39 -9
- package/src/tui.ts +13 -2
- package/src/utils.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.1.20] - 2026-06-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Recognized Warp (`TERM_PROGRAM=WarpTerminal`) as a first-class terminal, enabling Kitty inline images on macOS/Linux while keeping Warp's unsafe OSC 8 hyperlinks and Windows Kitty graphics disabled ([#3471](https://github.com/can1357/oh-my-pi/issues/3471)).
|
|
10
|
+
- Kept queued interrupt keys ahead of ordinary repaints so a slow long-transcript frame cannot consume the Ctrl+C/Esc double-press window before the second key is handled.
|
|
11
|
+
|
|
5
12
|
## [16.1.19] - 2026-06-25
|
|
6
13
|
|
|
7
14
|
### Fixed
|
|
@@ -8,7 +8,7 @@ export declare enum NotifyProtocol {
|
|
|
8
8
|
Osc99 = "\u001B]99;;",
|
|
9
9
|
Osc9 = "\u001B]9;"
|
|
10
10
|
}
|
|
11
|
-
export type TerminalId = "kitty" | "ghostty" | "wezterm" | "iterm2" | "vscode" | "alacritty" | "base" | "trueColor";
|
|
11
|
+
export type TerminalId = "kitty" | "ghostty" | "wezterm" | "iterm2" | "vscode" | "alacritty" | "warp" | "base" | "trueColor";
|
|
12
12
|
/** Terminal capability details used for rendering and protocol selection. */
|
|
13
13
|
export declare class TerminalInfo {
|
|
14
14
|
readonly id: TerminalId;
|
|
@@ -136,6 +136,8 @@ export declare function hyperlinksUserOverride(env?: NodeJS.ProcessEnv): boolean
|
|
|
136
136
|
* 7. Otherwise honor the static terminal capability.
|
|
137
137
|
*/
|
|
138
138
|
export declare function shouldEnableHyperlinksByDefault(env?: NodeJS.ProcessEnv, terminalId?: TerminalId): boolean;
|
|
139
|
+
/** Resolve terminal identity from environment markers used by common emulators. */
|
|
140
|
+
export declare function detectTerminalId(env?: NodeJS.ProcessEnv): TerminalId;
|
|
139
141
|
export declare const TERMINAL_ID: TerminalId;
|
|
140
142
|
/**
|
|
141
143
|
* The process-wide {@link TERMINAL} singleton: a {@link TerminalInfo} whose
|
|
@@ -169,7 +171,7 @@ export declare function setTerminalScreenToScrollback(enabled: boolean): void;
|
|
|
169
171
|
* capability); tests flip it directly to exercise the scaled-heading path.
|
|
170
172
|
*/
|
|
171
173
|
export declare function setTerminalTextSizing(enabled: boolean): void;
|
|
172
|
-
export declare function getTerminalInfo(terminalId: TerminalId): TerminalInfo;
|
|
174
|
+
export declare function getTerminalInfo(terminalId: TerminalId, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): TerminalInfo;
|
|
173
175
|
export interface CellDimensions {
|
|
174
176
|
widthPx: number;
|
|
175
177
|
heightPx: number;
|
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.1.
|
|
4
|
+
"version": "16.1.21",
|
|
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.1.
|
|
41
|
-
"@oh-my-pi/pi-utils": "16.1.
|
|
40
|
+
"@oh-my-pi/pi-natives": "16.1.21",
|
|
41
|
+
"@oh-my-pi/pi-utils": "16.1.21",
|
|
42
42
|
"lru-cache": "11.5.1",
|
|
43
43
|
"marked": "^18.0.5"
|
|
44
44
|
},
|
|
@@ -21,7 +21,16 @@ export enum NotifyProtocol {
|
|
|
21
21
|
Osc9 = "\x1b]9;",
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export type TerminalId =
|
|
24
|
+
export type TerminalId =
|
|
25
|
+
| "kitty"
|
|
26
|
+
| "ghostty"
|
|
27
|
+
| "wezterm"
|
|
28
|
+
| "iterm2"
|
|
29
|
+
| "vscode"
|
|
30
|
+
| "alacritty"
|
|
31
|
+
| "warp"
|
|
32
|
+
| "base"
|
|
33
|
+
| "trueColor";
|
|
25
34
|
|
|
26
35
|
function hasNeedleBefore(line: string, needle: string, limit: number): boolean {
|
|
27
36
|
const index = line.indexOf(needle);
|
|
@@ -251,9 +260,9 @@ export function shouldEnableSynchronizedOutputByDefault(
|
|
|
251
260
|
case "vscode":
|
|
252
261
|
return true;
|
|
253
262
|
default:
|
|
254
|
-
// VTE family, GNU screen, Apple Terminal, legacy native console
|
|
255
|
-
// (no WT_SESSION), and bare/unknown xterm profiles stay off until
|
|
256
|
-
// DECRQM probe proves support.
|
|
263
|
+
// VTE family, GNU screen, Apple Terminal, Warp, legacy native console
|
|
264
|
+
// host (no WT_SESSION), and bare/unknown xterm profiles stay off until
|
|
265
|
+
// the DECRQM probe proves support.
|
|
257
266
|
return false;
|
|
258
267
|
}
|
|
259
268
|
}
|
|
@@ -374,6 +383,18 @@ function getFallbackImageProtocol(terminalId: TerminalId): ImageProtocol | null
|
|
|
374
383
|
}
|
|
375
384
|
return null;
|
|
376
385
|
}
|
|
386
|
+
function getWarpTerminalInfo(platform: NodeJS.Platform, env: NodeJS.ProcessEnv = Bun.env): TerminalInfo {
|
|
387
|
+
// Warp for Windows still drives WSL shells from the Windows renderer, where
|
|
388
|
+
// the Kitty APC sequences print as visible garbage. Detect that case via the
|
|
389
|
+
// WSL host markers (Bun reports `process.platform === "linux"` inside WSL)
|
|
390
|
+
// and treat it the same as native win32.
|
|
391
|
+
const windowsHost =
|
|
392
|
+
platform === "win32" || (platform === "linux" && Boolean(env.WSL_DISTRO_NAME || env.WSL_INTEROP));
|
|
393
|
+
return windowsHost
|
|
394
|
+
? new TerminalInfo("warp", null, true, false, NotifyProtocol.Bell)
|
|
395
|
+
: new TerminalInfo("warp", ImageProtocol.Kitty, true, false, NotifyProtocol.Bell);
|
|
396
|
+
}
|
|
397
|
+
|
|
377
398
|
const KNOWN_TERMINALS = Object.freeze({
|
|
378
399
|
// Fallback terminals
|
|
379
400
|
base: new TerminalInfo("base", null, false, false, NotifyProtocol.Bell),
|
|
@@ -385,9 +406,11 @@ const KNOWN_TERMINALS = Object.freeze({
|
|
|
385
406
|
iterm2: new TerminalInfo("iterm2", ImageProtocol.Iterm2, true, true, NotifyProtocol.Osc9),
|
|
386
407
|
vscode: new TerminalInfo("vscode", null, true, true, NotifyProtocol.Bell),
|
|
387
408
|
alacritty: new TerminalInfo("alacritty", null, true, true, NotifyProtocol.Bell),
|
|
409
|
+
warp: getWarpTerminalInfo(process.platform),
|
|
388
410
|
});
|
|
389
411
|
|
|
390
|
-
|
|
412
|
+
/** Resolve terminal identity from environment markers used by common emulators. */
|
|
413
|
+
export function detectTerminalId(env: NodeJS.ProcessEnv = Bun.env): TerminalId {
|
|
391
414
|
function caseEq(a: string, b: string): boolean {
|
|
392
415
|
return a.toLowerCase() === b.toLowerCase(); // For compiler to pattern match
|
|
393
416
|
}
|
|
@@ -402,7 +425,7 @@ export const TERMINAL_ID: TerminalId = (() => {
|
|
|
402
425
|
TERM_PROGRAM,
|
|
403
426
|
TERM,
|
|
404
427
|
COLORTERM,
|
|
405
|
-
} =
|
|
428
|
+
} = env;
|
|
406
429
|
|
|
407
430
|
if (KITTY_WINDOW_ID) return "kitty";
|
|
408
431
|
if (GHOSTTY_RESOURCES_DIR) return "ghostty";
|
|
@@ -418,6 +441,7 @@ export const TERMINAL_ID: TerminalId = (() => {
|
|
|
418
441
|
if (caseEq(TERM_PROGRAM, "iterm.app")) return "iterm2";
|
|
419
442
|
if (caseEq(TERM_PROGRAM, "vscode")) return "vscode";
|
|
420
443
|
if (caseEq(TERM_PROGRAM, "alacritty")) return "alacritty";
|
|
444
|
+
if (caseEq(TERM_PROGRAM, "WarpTerminal")) return "warp";
|
|
421
445
|
}
|
|
422
446
|
|
|
423
447
|
if (TERM?.toLowerCase().includes("ghostty")) return "ghostty";
|
|
@@ -426,7 +450,9 @@ export const TERMINAL_ID: TerminalId = (() => {
|
|
|
426
450
|
if (caseEq(COLORTERM, "truecolor") || caseEq(COLORTERM, "24bit")) return "trueColor";
|
|
427
451
|
}
|
|
428
452
|
return "base";
|
|
429
|
-
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export const TERMINAL_ID: TerminalId = detectTerminalId(Bun.env);
|
|
430
456
|
|
|
431
457
|
/**
|
|
432
458
|
* The process-wide {@link TERMINAL} singleton: a {@link TerminalInfo} whose
|
|
@@ -504,8 +530,12 @@ export function setTerminalTextSizing(enabled: boolean): void {
|
|
|
504
530
|
TERMINAL.textSizing = enabled;
|
|
505
531
|
}
|
|
506
532
|
|
|
507
|
-
export function getTerminalInfo(
|
|
508
|
-
|
|
533
|
+
export function getTerminalInfo(
|
|
534
|
+
terminalId: TerminalId,
|
|
535
|
+
platform: NodeJS.Platform = process.platform,
|
|
536
|
+
env: NodeJS.ProcessEnv = Bun.env,
|
|
537
|
+
): TerminalInfo {
|
|
538
|
+
return terminalId === "warp" ? getWarpTerminalInfo(platform, env) : KNOWN_TERMINALS[terminalId];
|
|
509
539
|
}
|
|
510
540
|
|
|
511
541
|
export interface CellDimensions {
|
package/src/tui.ts
CHANGED
|
@@ -924,6 +924,8 @@ export class TUI extends Container {
|
|
|
924
924
|
#renderScheduler: RenderScheduler;
|
|
925
925
|
#lastRenderAt = 0;
|
|
926
926
|
static readonly #MIN_RENDER_INTERVAL_MS = 1000 / 30;
|
|
927
|
+
static readonly #INPUT_RENDER_GRACE_MS = TUI.#MIN_RENDER_INTERVAL_MS;
|
|
928
|
+
#inputRenderGraceUntilMs = 0;
|
|
927
929
|
// Pane-reflow settle window for tmux/screen/zellij. The host process gets
|
|
928
930
|
// SIGWINCH (and `process.stdout` already reports the new geometry) before
|
|
929
931
|
// the multiplexer finishes repainting the pane at the new size, and
|
|
@@ -2109,8 +2111,11 @@ export class TUI extends Container {
|
|
|
2109
2111
|
if (this.#multiplexerResizeTimer) {
|
|
2110
2112
|
return;
|
|
2111
2113
|
}
|
|
2112
|
-
const
|
|
2113
|
-
const
|
|
2114
|
+
const now = this.#renderScheduler.now();
|
|
2115
|
+
const elapsed = now - this.#lastRenderAt;
|
|
2116
|
+
const cadenceDelay = Math.max(0, TUI.#MIN_RENDER_INTERVAL_MS - elapsed);
|
|
2117
|
+
const inputGraceDelay = Math.max(0, this.#inputRenderGraceUntilMs - now);
|
|
2118
|
+
const delay = Math.max(cadenceDelay, inputGraceDelay);
|
|
2114
2119
|
this.#renderTimer = this.#renderScheduler.scheduleRender(() => {
|
|
2115
2120
|
this.#renderTimer = undefined;
|
|
2116
2121
|
if (this.#stopped || !this.#renderRequested) {
|
|
@@ -2126,6 +2131,12 @@ export class TUI extends Container {
|
|
|
2126
2131
|
}
|
|
2127
2132
|
|
|
2128
2133
|
#handleInput(data: string): void {
|
|
2134
|
+
// Raw-mode Ctrl+C/Esc arrive as stdin data, not process signals. If the
|
|
2135
|
+
// first key in a double-key gesture schedules an immediate slow repaint,
|
|
2136
|
+
// the queued second key can sit behind that repaint long enough for the
|
|
2137
|
+
// app-level double-press window to expire. Give the input queue one frame
|
|
2138
|
+
// before ordinary paints; forced repaints still bypass this path.
|
|
2139
|
+
this.#inputRenderGraceUntilMs = this.#renderScheduler.now() + TUI.#INPUT_RENDER_GRACE_MS;
|
|
2129
2140
|
if (this.#inputListeners.size > 0) {
|
|
2130
2141
|
let current = data;
|
|
2131
2142
|
for (const listener of this.#inputListeners) {
|
package/src/utils.ts
CHANGED
|
@@ -121,7 +121,7 @@ export function truncateToWidth(
|
|
|
121
121
|
return nativeTruncateToWidth(
|
|
122
122
|
text,
|
|
123
123
|
maxWidth,
|
|
124
|
-
(ellipsisKind === "" ? Ellipsis.Omit : ellipsisKind) ?? Ellipsis.Unicode,
|
|
124
|
+
(typeof ellipsisKind === "string" ? Ellipsis.Omit : ellipsisKind) ?? Ellipsis.Unicode,
|
|
125
125
|
pad ?? false,
|
|
126
126
|
DEFAULT_TAB_WIDTH,
|
|
127
127
|
);
|