@oh-my-pi/pi-tui 17.2.10 → 17.2.12
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 +14 -0
- package/dist/types/terminal.d.ts +14 -12
- package/package.json +3 -3
- package/src/components/loader.ts +2 -3
- package/src/latex-block.ts +116 -6
- package/src/terminal-capabilities.ts +3 -3
- package/src/terminal.ts +32 -15
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.2.12] - 2026-08-08
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed slow Loader paints exceeding their cost-aware CPU duty cycle on WSL/ConPTY when a 200 ms backpressure cap was shorter than the proportional delay ([#8012](https://github.com/can1357/oh-my-pi/issues/8012)).
|
|
10
|
+
- Fixed display-math (`$$…$$`) fractions rendering as fragmented text when the numerator and denominator are written on separate source lines: `latexToBlock` treated the top-level newline between `\frac{num}` and `{den}` as a row break, severing `\frac` from its denominator. Such argument-continuation newlines are now preserved so the fraction stays stacked ([#7996](https://github.com/can1357/oh-my-pi/issues/7996)).
|
|
11
|
+
|
|
12
|
+
## [17.2.11] - 2026-08-07
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Fixed an issue where Herdr panes lost native terminal scrollback during TUI transcript replacements or resize redraws.
|
|
17
|
+
- Fixed an issue inside tmux where explicit display resets retained stale light/dark palettes and leaked terminal capability bytes into the editor.
|
|
18
|
+
|
|
5
19
|
## [17.2.10] - 2026-08-06
|
|
6
20
|
|
|
7
21
|
### Fixed
|
package/dist/types/terminal.d.ts
CHANGED
|
@@ -101,12 +101,13 @@ export interface Terminal {
|
|
|
101
101
|
*/
|
|
102
102
|
onAppearanceReport?(callback: (appearance: TerminalAppearance, requestToken?: TerminalAppearanceRequestToken) => void): (() => void) | void;
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
104
|
+
* Start a bounded OSC 11 background-color refresh cycle, driving appearance
|
|
105
105
|
* callbacks through the same parse/dedup pipeline used at startup and on Mode
|
|
106
|
-
* 2031 notifications.
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
* without a
|
|
106
|
+
* 2031 notifications. Direct terminals need one query; tmux needs a
|
|
107
|
+
* passthrough query to update its cache followed by one delayed direct cache
|
|
108
|
+
* read. Invoked on the user's explicit display-reset gesture so terminals
|
|
109
|
+
* without end-to-end Mode 2031 notifications pick up a light/dark switch
|
|
110
|
+
* without a restart. No periodic probes are armed.
|
|
110
111
|
*
|
|
111
112
|
* A caller-provided token must be propagated unchanged to callbacks and
|
|
112
113
|
* returned when the request is accepted. This lets callers establish ownership
|
|
@@ -146,13 +147,14 @@ export declare class ProcessTerminal implements Terminal {
|
|
|
146
147
|
onAppearanceChange(callback: (appearance: TerminalAppearance, requestToken?: TerminalAppearanceRequestToken) => void): void;
|
|
147
148
|
onAppearanceReport(callback: (appearance: TerminalAppearance, requestToken?: TerminalAppearanceRequestToken) => void): () => void;
|
|
148
149
|
/**
|
|
149
|
-
* Re-query the terminal background
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
150
|
+
* Re-query the terminal background through the startup DA1-sentinel FIFO,
|
|
151
|
+
* pending/queued gating, parsing, dedup, and appearance callbacks. Inside
|
|
152
|
+
* tmux, only this explicit path first passes an OSC 11 query to the outer
|
|
153
|
+
* terminal, waits briefly for tmux to consume the response into its cache,
|
|
154
|
+
* then reads that cache with a direct query. The outer query deliberately has
|
|
155
|
+
* no DA1 sentinel: multiplexers can decode a fragmented DA1 response as a key
|
|
156
|
+
* sequence and leak the remaining bytes into the editor. Startup and Mode 2031
|
|
157
|
+
* probes remain direct. Suppressed while inactive, headless, or after teardown.
|
|
156
158
|
*/
|
|
157
159
|
refreshAppearance(requestToken?: TerminalAppearanceRequestToken): TerminalAppearanceRequestToken | void;
|
|
158
160
|
onPrivateModeReport(callback: (mode: number, supported: boolean, confirmed?: boolean) => void): 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": "17.2.
|
|
4
|
+
"version": "17.2.12",
|
|
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.
|
|
41
|
-
"@oh-my-pi/pi-utils": "17.2.
|
|
40
|
+
"@oh-my-pi/pi-natives": "17.2.12",
|
|
41
|
+
"@oh-my-pi/pi-utils": "17.2.12"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"ghostty-web": "^0.4.0"
|
package/src/components/loader.ts
CHANGED
|
@@ -4,7 +4,6 @@ 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
7
|
const RENDER_BACKPRESSURE_MULTIPLIER = 9;
|
|
9
8
|
|
|
10
9
|
type ColorFn = (str: string) => string;
|
|
@@ -144,8 +143,8 @@ export class Loader extends Text {
|
|
|
144
143
|
if (this.#intervalId !== timer) return;
|
|
145
144
|
const cadenceDelayMs = Math.max(0, intervalMs - frameCostMs);
|
|
146
145
|
// Idle for nine times the paint cost to keep animation at or below
|
|
147
|
-
// 10% CPU,
|
|
148
|
-
const backpressureDelayMs =
|
|
146
|
+
// 10% CPU, even when a slow ConPTY write exceeds the normal cadence.
|
|
147
|
+
const backpressureDelayMs = frameCostMs * RENDER_BACKPRESSURE_MULTIPLIER;
|
|
149
148
|
this.#scheduleTick(intervalMs, Math.max(cadenceDelayMs, backpressureDelayMs));
|
|
150
149
|
}, delayMs);
|
|
151
150
|
this.#intervalId = timer;
|
package/src/latex-block.ts
CHANGED
|
@@ -146,6 +146,16 @@ const HBRACE_COMMANDS: Record<string, HBraceSpec> = {
|
|
|
146
146
|
underparen: { left: "╰", mid: "─", center: "─", right: "╯", over: false },
|
|
147
147
|
};
|
|
148
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Number of required arguments each display command consumes. Shared by
|
|
151
|
+
* {@link readArg} and {@link splitLines} so nested command atoms consume exactly
|
|
152
|
+
* their own arguments while preserving any outer command's pending arity.
|
|
153
|
+
*/
|
|
154
|
+
const COMMAND_ARITY: Record<string, number> = { overset: 2, underset: 2, stackrel: 2, sqrt: 1 };
|
|
155
|
+
for (const name in FRAC_COMMANDS) COMMAND_ARITY[name] = 2;
|
|
156
|
+
for (const name in BINOM_COMMANDS) COMMAND_ARITY[name] = 2;
|
|
157
|
+
for (const name in HBRACE_COMMANDS) COMMAND_ARITY[name] = 1;
|
|
158
|
+
|
|
149
159
|
// Vertical delimiter piece characters: `only` for single-line content, then
|
|
150
160
|
// top/mid/bot columns for stretched forms; `axis` replaces `mid` at the
|
|
151
161
|
// baseline row (the brace point).
|
|
@@ -516,11 +526,13 @@ function readBraceGroup(src: string, i: number): Span {
|
|
|
516
526
|
|
|
517
527
|
/**
|
|
518
528
|
* Read one command argument: a `{…}` group, a single char, or a `\command`
|
|
519
|
-
* together with its
|
|
520
|
-
*
|
|
529
|
+
* together with its arguments (or whole `\begin…\end` block). Commands whose
|
|
530
|
+
* arity is known consume exactly that many arguments, including across source
|
|
531
|
+
* whitespace, so `\frac\sqrt {a} {b}` reads `\sqrt {a}` as the numerator and
|
|
532
|
+
* leaves `{b}` for the denominator.
|
|
521
533
|
*/
|
|
522
534
|
function readArg(src: string, i: number): Span {
|
|
523
|
-
while (src[i] === " ") i++;
|
|
535
|
+
while (src[i] === " " || src[i] === "\t" || src[i] === "\n") i++;
|
|
524
536
|
if (i >= src.length) return { text: "", end: i };
|
|
525
537
|
if (src[i] === "{") return readBraceGroup(src, i);
|
|
526
538
|
if (src[i] !== "\\") return { text: src[i], end: i + 1 };
|
|
@@ -535,6 +547,22 @@ function readArg(src: string, i: number): Span {
|
|
|
535
547
|
if (env) return env;
|
|
536
548
|
}
|
|
537
549
|
if (!name) return { text: src.slice(i, i + 2), end: i + 2 }; // non-letter command (\,, \{, …)
|
|
550
|
+
|
|
551
|
+
const arity = COMMAND_ARITY[name];
|
|
552
|
+
if (arity !== undefined) {
|
|
553
|
+
let end = j;
|
|
554
|
+
// Optional command arguments (e.g. the degree in `\sqrt[3]{x}`) do not
|
|
555
|
+
// consume a required-argument slot.
|
|
556
|
+
for (;;) {
|
|
557
|
+
while (src[end] === " " || src[end] === "\t" || src[end] === "\n") end++;
|
|
558
|
+
if (src[end] !== "[") break;
|
|
559
|
+
const close = src.indexOf("]", end);
|
|
560
|
+
end = close === -1 ? src.length : close + 1;
|
|
561
|
+
}
|
|
562
|
+
for (let arg = 0; arg < arity; arg++) end = readArg(src, end).end;
|
|
563
|
+
return { text: src.slice(i, end), end };
|
|
564
|
+
}
|
|
565
|
+
|
|
538
566
|
let end = j;
|
|
539
567
|
while (src[end] === "[" || src[end] === "{") {
|
|
540
568
|
if (src[end] === "{") end = readBraceGroup(src, end).end;
|
|
@@ -1271,6 +1299,80 @@ function parseExpr(src: string, ctx: Ctx = ROOT_CTX): Box {
|
|
|
1271
1299
|
return hconcat(boxes);
|
|
1272
1300
|
}
|
|
1273
1301
|
|
|
1302
|
+
/**
|
|
1303
|
+
* Count the command arguments still owed at the end of `seg` — non-zero when
|
|
1304
|
+
* the row ends mid-construct (`\frac{a}` awaiting its denominator, or
|
|
1305
|
+
* `\frac`/`x^` awaiting any argument). Pending arities form a stack: an
|
|
1306
|
+
* unbraced nested command consumes one outer argument, then retains its own
|
|
1307
|
+
* pending arguments without discarding the outer command's remaining arity.
|
|
1308
|
+
* Used to keep a command joined to an argument written on the next source line
|
|
1309
|
+
* while still treating an ordinary next row (`a\n{b+c}`) as a real row break.
|
|
1310
|
+
*/
|
|
1311
|
+
function bracesOwed(seg: string): number {
|
|
1312
|
+
const pending: number[] = [];
|
|
1313
|
+
const consumeArg = (): void => {
|
|
1314
|
+
const top = pending.length - 1;
|
|
1315
|
+
if (top < 0) return;
|
|
1316
|
+
if (pending[top] === 1) pending.pop();
|
|
1317
|
+
else pending[top]--;
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
let i = 0;
|
|
1321
|
+
while (i < seg.length) {
|
|
1322
|
+
const c = seg[i];
|
|
1323
|
+
if (c === "\\") {
|
|
1324
|
+
let j = i + 1;
|
|
1325
|
+
let name = "";
|
|
1326
|
+
while (j < seg.length && /[A-Za-z]/.test(seg[j])) name += seg[j++];
|
|
1327
|
+
// A command plus its immediately attached `[…]`/`{…}` groups is one
|
|
1328
|
+
// atom for an enclosing argument, matching readArg. Consume that outer
|
|
1329
|
+
// argument first, then retain only the command's own missing arguments
|
|
1330
|
+
// in a nested frame. Attached groups beyond the known arity still stay
|
|
1331
|
+
// part of the atom and cannot consume another outer argument.
|
|
1332
|
+
consumeArg();
|
|
1333
|
+
const arity = name ? (COMMAND_ARITY[name] ?? 0) : 0;
|
|
1334
|
+
let attached = 0;
|
|
1335
|
+
if (name) {
|
|
1336
|
+
while (seg[j] === "[" || seg[j] === "{") {
|
|
1337
|
+
if (seg[j] === "{") {
|
|
1338
|
+
j = readBraceGroup(seg, j).end;
|
|
1339
|
+
if (attached < arity) attached++;
|
|
1340
|
+
} else {
|
|
1341
|
+
const close = seg.indexOf("]", j);
|
|
1342
|
+
j = close === -1 ? seg.length : close + 1;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
} else {
|
|
1346
|
+
j = i + 2; // non-letter command (`\,`, `\{`, …)
|
|
1347
|
+
}
|
|
1348
|
+
const missing = arity - attached;
|
|
1349
|
+
if (missing > 0) pending.push(missing);
|
|
1350
|
+
i = j;
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
if (c === "{") {
|
|
1354
|
+
i = readBraceGroup(seg, i).end;
|
|
1355
|
+
consumeArg();
|
|
1356
|
+
continue;
|
|
1357
|
+
}
|
|
1358
|
+
if (c === "^" || c === "_") {
|
|
1359
|
+
pending.push(1);
|
|
1360
|
+
i++;
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
if (c === " " || c === "\t" || c === "\n") {
|
|
1364
|
+
i++;
|
|
1365
|
+
continue;
|
|
1366
|
+
}
|
|
1367
|
+
consumeArg(); // a bare atom satisfies one pending argument
|
|
1368
|
+
i++;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
let owed = 0;
|
|
1372
|
+
for (const remaining of pending) owed += remaining;
|
|
1373
|
+
return owed;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1274
1376
|
/** Split on top-level `\n` and `\\` row separators (outside braces and environments). */
|
|
1275
1377
|
function splitLines(src: string): string[] {
|
|
1276
1378
|
const lines: string[] = [];
|
|
@@ -1308,8 +1410,16 @@ function splitLines(src: string): string[] {
|
|
|
1308
1410
|
if (c === "{") braceDepth++;
|
|
1309
1411
|
else if (c === "}") braceDepth--;
|
|
1310
1412
|
else if (c === "\n" && braceDepth === 0 && envDepth === 0) {
|
|
1311
|
-
|
|
1312
|
-
|
|
1413
|
+
// A top-level newline is a row break UNLESS the current row ends with a
|
|
1414
|
+
// command still awaiting an argument (e.g. `\frac{num}\n{den}`,
|
|
1415
|
+
// `\frac{num}\n\sqrt{x}`, or `x^\n2`). Splitting there would sever the
|
|
1416
|
+
// command from its argument, so keep both in one segment; latexToBlock
|
|
1417
|
+
// collapses the interior newline to a space before parsing. A row that
|
|
1418
|
+
// merely opens with a braced group (`a\n{b+c}`) stays a break.
|
|
1419
|
+
if (bracesOwed(src.slice(last, i)) === 0) {
|
|
1420
|
+
lines.push(src.slice(last, i));
|
|
1421
|
+
last = i + 1;
|
|
1422
|
+
}
|
|
1313
1423
|
}
|
|
1314
1424
|
i++;
|
|
1315
1425
|
}
|
|
@@ -1327,7 +1437,7 @@ function splitLines(src: string): string[] {
|
|
|
1327
1437
|
export function latexToBlock(src: string): string[] {
|
|
1328
1438
|
if (typeof src !== "string" || src.trim() === "") return [];
|
|
1329
1439
|
const rows = splitLines(src.trim())
|
|
1330
|
-
.map(line => line.trim())
|
|
1440
|
+
.map(line => line.replace(/[ \t]*\n[ \t]*/g, " ").trim())
|
|
1331
1441
|
.filter(line => line !== "")
|
|
1332
1442
|
.map(line => parseExpr(line));
|
|
1333
1443
|
if (rows.length === 0) return [];
|
|
@@ -187,12 +187,12 @@ export class TerminalInfo {
|
|
|
187
187
|
|
|
188
188
|
/** Detect terminal multiplexers where scrollback clearing and height-change redraws are hostile. */
|
|
189
189
|
export function isInsideTerminalMultiplexer(env: NodeJS.ProcessEnv = Bun.env): boolean {
|
|
190
|
-
// TMUX/STY/ZELLIJ and CMUX workspace/surface/remote-transport
|
|
191
|
-
// authoritative session signals. TERM can also survive when those are
|
|
190
|
+
// TMUX/STY/ZELLIJ, Herdr, and CMUX workspace/surface/remote-transport
|
|
191
|
+
// markers are authoritative session signals. TERM can also survive when those are
|
|
192
192
|
// stripped (`sudo` without -E, `su`, env-sanitizing launchers/ssh). Do not
|
|
193
193
|
// use CMUX_SOCKET_PATH here: it is a CLI socket override and can be set
|
|
194
194
|
// outside a CMUX terminal.
|
|
195
|
-
if (env.TMUX || env.STY || env.ZELLIJ) return true;
|
|
195
|
+
if (env.TMUX || env.STY || env.ZELLIJ || env.HERDR_ENV === "1") return true;
|
|
196
196
|
if (env.CMUX_WORKSPACE_ID || env.CMUX_SURFACE_ID || env.CMUX_REMOTE_TRANSPORT) return true;
|
|
197
197
|
const term = env.TERM?.toLowerCase() ?? "";
|
|
198
198
|
return term.startsWith("tmux") || term.startsWith("screen");
|
package/src/terminal.ts
CHANGED
|
@@ -459,12 +459,13 @@ export interface Terminal {
|
|
|
459
459
|
callback: (appearance: TerminalAppearance, requestToken?: TerminalAppearanceRequestToken) => void,
|
|
460
460
|
): (() => void) | void;
|
|
461
461
|
/**
|
|
462
|
-
*
|
|
462
|
+
* Start a bounded OSC 11 background-color refresh cycle, driving appearance
|
|
463
463
|
* callbacks through the same parse/dedup pipeline used at startup and on Mode
|
|
464
|
-
* 2031 notifications.
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
* without a
|
|
464
|
+
* 2031 notifications. Direct terminals need one query; tmux needs a
|
|
465
|
+
* passthrough query to update its cache followed by one delayed direct cache
|
|
466
|
+
* read. Invoked on the user's explicit display-reset gesture so terminals
|
|
467
|
+
* without end-to-end Mode 2031 notifications pick up a light/dark switch
|
|
468
|
+
* without a restart. No periodic probes are armed.
|
|
468
469
|
*
|
|
469
470
|
* A caller-provided token must be propagated unchanged to callbacks and
|
|
470
471
|
* returned when the request is accepted. This lets callers establish ownership
|
|
@@ -517,6 +518,7 @@ function parseOsc99KeyValues(section: string): Map<string, string> {
|
|
|
517
518
|
}
|
|
518
519
|
const XTERM_SCROLL_TO_BOTTOM_MODES = [1010, 1011] as const;
|
|
519
520
|
type Osc11QueryRoute = "direct" | "tmux";
|
|
521
|
+
const TMUX_OSC11_CACHE_REFRESH_DELAY_MS = 100;
|
|
520
522
|
|
|
521
523
|
function isXtermScrollToBottomMode(mode: number): boolean {
|
|
522
524
|
return mode === 1010 || mode === 1011;
|
|
@@ -596,6 +598,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
596
598
|
#osc11QueuedQuery?: { route: Osc11QueryRoute; token?: TerminalAppearanceRequestToken };
|
|
597
599
|
#nextAppearanceRequestToken = 1;
|
|
598
600
|
#osc11ResponseBuffer = "";
|
|
601
|
+
#osc11TmuxRefreshTimer?: Timer;
|
|
599
602
|
#osc99PendingId: string | undefined;
|
|
600
603
|
#osc99ResponseBuffer = "";
|
|
601
604
|
#osc99Capabilities = new Map<string, string>();
|
|
@@ -671,13 +674,14 @@ export class ProcessTerminal implements Terminal {
|
|
|
671
674
|
}
|
|
672
675
|
|
|
673
676
|
/**
|
|
674
|
-
* Re-query the terminal background
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
*
|
|
677
|
+
* Re-query the terminal background through the startup DA1-sentinel FIFO,
|
|
678
|
+
* pending/queued gating, parsing, dedup, and appearance callbacks. Inside
|
|
679
|
+
* tmux, only this explicit path first passes an OSC 11 query to the outer
|
|
680
|
+
* terminal, waits briefly for tmux to consume the response into its cache,
|
|
681
|
+
* then reads that cache with a direct query. The outer query deliberately has
|
|
682
|
+
* no DA1 sentinel: multiplexers can decode a fragmented DA1 response as a key
|
|
683
|
+
* sequence and leak the remaining bytes into the editor. Startup and Mode 2031
|
|
684
|
+
* probes remain direct. Suppressed while inactive, headless, or after teardown.
|
|
681
685
|
*/
|
|
682
686
|
refreshAppearance(requestToken?: TerminalAppearanceRequestToken): TerminalAppearanceRequestToken | void {
|
|
683
687
|
if (!this.#active || this.#headless || this.#dead) return;
|
|
@@ -1041,7 +1045,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
1041
1045
|
switch (owner.kind) {
|
|
1042
1046
|
case "osc11": {
|
|
1043
1047
|
if (this.#osc11Pending) {
|
|
1044
|
-
// DA1 arrived before
|
|
1048
|
+
// DA1 arrived before OSC 11 response: terminal doesn't support OSC 11.
|
|
1045
1049
|
this.#osc11Pending = false;
|
|
1046
1050
|
this.#osc11ActiveToken = undefined;
|
|
1047
1051
|
this.#osc11ResponseBuffer = "";
|
|
@@ -1221,11 +1225,20 @@ export class ProcessTerminal implements Terminal {
|
|
|
1221
1225
|
this.#osc11Pending = true;
|
|
1222
1226
|
this.#osc11ActiveToken = token;
|
|
1223
1227
|
this.#osc11ResponseBuffer = "";
|
|
1224
|
-
this.#da1SentinelOwners.push({ kind: "osc11" });
|
|
1225
1228
|
if (route === "tmux") {
|
|
1226
|
-
this.#safeWrite(wrapTmuxPassthrough("\x1b]11;?\x07
|
|
1229
|
+
this.#safeWrite(wrapTmuxPassthrough("\x1b]11;?\x07"));
|
|
1230
|
+
this.#osc11TmuxRefreshTimer = setTimeout(() => {
|
|
1231
|
+
this.#osc11TmuxRefreshTimer = undefined;
|
|
1232
|
+
if (this.#dead || !this.#osc11Pending) return;
|
|
1233
|
+
this.#startDirectOsc11Query();
|
|
1234
|
+
}, TMUX_OSC11_CACHE_REFRESH_DELAY_MS);
|
|
1227
1235
|
return;
|
|
1228
1236
|
}
|
|
1237
|
+
this.#startDirectOsc11Query();
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
#startDirectOsc11Query(): void {
|
|
1241
|
+
this.#da1SentinelOwners.push({ kind: "osc11" });
|
|
1229
1242
|
this.#safeWrite("\x1b]11;?\x07"); // OSC 11 query (BEL terminated)
|
|
1230
1243
|
this.#safeWrite("\x1b[c"); // DA1 sentinel
|
|
1231
1244
|
}
|
|
@@ -1562,6 +1575,10 @@ export class ProcessTerminal implements Terminal {
|
|
|
1562
1575
|
clearTimeout(this.#mode2031DebounceTimer);
|
|
1563
1576
|
this.#mode2031DebounceTimer = undefined;
|
|
1564
1577
|
}
|
|
1578
|
+
if (this.#osc11TmuxRefreshTimer) {
|
|
1579
|
+
clearTimeout(this.#osc11TmuxRefreshTimer);
|
|
1580
|
+
this.#osc11TmuxRefreshTimer = undefined;
|
|
1581
|
+
}
|
|
1565
1582
|
this.#appearanceCallbacks = [];
|
|
1566
1583
|
this.#appearanceReportCallbacks = [];
|
|
1567
1584
|
this.#osc11Pending = false;
|