Merge remote-tracking branch 'origin/farm/158a3a86/osc-99-probe-tmux-gate'
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
### Fixed
|
||||
|
||||
- Fixed SIXEL image rendering stripping a horizontal slice from images when the cell height was not a multiple of 6. SIXEL encodes in 6-pixel vertical bands; a non-multiple-of-6 height was padded, causing the terminal to allocate an extra row that the TUI did not reserve — the next line of content then overwrote the bottom of the image. The encode height is now rounded down to the largest multiple of 6 within the requested row budget, eliminating padding without exceeding the caller's height cap, and the width is scaled by the same ratio to preserve the image aspect ratio.
|
||||
- Fixed the Kitty OSC 99 desktop-notification capability probe (`p=?`) firing under tmux/screen, where the multiplexer forwards the passthrough query to the outer terminal but cannot route the reply back to the sending pane — the capability list leaked into the pane as text and its bytes switched panes ([#5582](https://github.com/can1357/oh-my-pi/issues/5582)). The probe is now suppressed inside a multiplexer (rich notifications fall back to the single-line OSC 99 form until confirmed), mirroring the graphics-probe fix in #5381.
|
||||
|
||||
## [16.5.2] - 2026-07-14
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@ import {
|
||||
import { setKittyProtocolActive } from "./keys";
|
||||
import { StdinBuffer } from "./stdin-buffer";
|
||||
import {
|
||||
isInsideTerminalMultiplexer,
|
||||
isInsideTmux,
|
||||
NotifyProtocol,
|
||||
setCellDimensions,
|
||||
setOsc99Supported,
|
||||
TERMINAL,
|
||||
wrapTmuxPassthrough,
|
||||
} from "./terminal-capabilities";
|
||||
import { type HangulCompatibilityJamoWidth, setHangulCompatibilityJamoWidth } from "./utils";
|
||||
|
||||
@@ -1042,6 +1042,15 @@ export class ProcessTerminal implements Terminal {
|
||||
|
||||
#shouldQueryOsc99Support(): boolean {
|
||||
if (TERMINAL.notifyProtocol !== NotifyProtocol.Osc99) return false;
|
||||
// Never probe inside a terminal multiplexer. tmux/screen forward the
|
||||
// passthrough-wrapped `p=?` query to the outer terminal, but cannot route
|
||||
// the capability reply back to the pane that sent it (tmux/tmux#4386,
|
||||
// tmux/tmux#3964), so the reply leaks into the pane as literal text and
|
||||
// its bytes perturb input (issue #5582 — the notification sibling of the
|
||||
// graphics-probe leak #5381). Rich notifications fall back to the
|
||||
// single-line OSC 99 form until confirmation, and delivery still uses the
|
||||
// passthrough/BEL path (#3395).
|
||||
if (isInsideTerminalMultiplexer($env)) return false;
|
||||
return !isBunTestRuntime() || $env.PI_TUI_OSC99_PROBE === "1";
|
||||
}
|
||||
|
||||
@@ -1055,14 +1064,9 @@ export class ProcessTerminal implements Terminal {
|
||||
const id = `omp-probe-${nextOsc99ProbeId++}`;
|
||||
this.#osc99PendingId = id;
|
||||
this.#da1SentinelOwners.push({ kind: "osc99Probe", id });
|
||||
// Wrap the probe under tmux so terminals behind `allow-passthrough on`
|
||||
// can still respond (mirroring how `TerminalInfo.sendNotification`
|
||||
// wraps notification deliveries). Without it the probe is swallowed
|
||||
// inside tmux even when the outer terminal speaks OSC 99, and rich
|
||||
// notifications stay permanently downgraded to the single-line fallback.
|
||||
const probe = `\x1b]99;i=${id}:p=?;\x1b\\`;
|
||||
const sequence = isInsideTmux() ? wrapTmuxPassthrough(probe) : probe;
|
||||
this.#safeWrite(`${sequence}\x1b[c`);
|
||||
// The probe never runs under a multiplexer (see #shouldQueryOsc99Support),
|
||||
// so it is always sent directly to the terminal.
|
||||
this.#safeWrite(`\x1b]99;i=${id}:p=?;\x1b\\\x1b[c`);
|
||||
}
|
||||
|
||||
#handleOsc99CapabilityResponse(metaRaw: string, payload: string): boolean {
|
||||
|
||||
@@ -291,16 +291,18 @@ describe("terminal notifications", () => {
|
||||
expect(writes).toEqual(["\x1b]99;;ping\x1b\\\x07"]);
|
||||
});
|
||||
|
||||
it("under tmux, the OSC 99 capability probe is wrapped in DCS passthrough", () => {
|
||||
it("under tmux, the OSC 99 capability probe is suppressed (reply cannot route back to the pane)", () => {
|
||||
Bun.env.PI_TUI_OSC99_PROBE = "1";
|
||||
Bun.env.TMUX = "/tmp/tmux-1000/default,1234,0";
|
||||
mutableTerminal.notifyProtocol = NotifyProtocol.Osc99;
|
||||
const { terminal, writes } = setupProcessTerminal();
|
||||
try {
|
||||
const probe = writes.find(
|
||||
w => w.startsWith("\x1bPtmux;\x1b\x1b]99;i=omp-probe-") && w.endsWith("\x1b\x1b\\\x1b\\\x1b[c"),
|
||||
);
|
||||
expect(probe).toBeDefined();
|
||||
// tmux forwards the passthrough probe to the outer terminal but cannot
|
||||
// route the `p=?` reply back to the sending pane, so the reply would
|
||||
// leak into the pane as text (#5582). The probe must not fire at all.
|
||||
const probe = writes.find(w => w.includes("]99;i=omp-probe-") && w.includes(":p=?"));
|
||||
expect(probe).toBeUndefined();
|
||||
expect(isOsc99Supported()).toBe(false);
|
||||
} finally {
|
||||
terminal.stop();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user