@oh-my-pi/pi-tui 18.1.22 → 18.2.1

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.
@@ -10,15 +10,36 @@ export function isInsideHerdr(env: NodeJS.ProcessEnv = Bun.env): boolean {
10
10
  return false;
11
11
  }
12
12
 
13
- /** Detect whether a terminal multiplexer owns the current screen grid. */
14
- export function isInsideTerminalMultiplexer(env: NodeJS.ProcessEnv = Bun.env): boolean {
15
- // TMUX/STY/ZELLIJ, Herdr, and CMUX workspace/surface/remote-transport
16
- // markers are authoritative session signals. TERM can also survive when those are
17
- // stripped (`sudo` without -E, `su`, env-sanitizing launchers/ssh). Do not
18
- // use CMUX_SOCKET_PATH here: it is a CLI socket override and can be set
19
- // outside a CMUX terminal.
20
- if (env.TMUX || env.STY || env.ZELLIJ || isInsideHerdr(env)) return true;
21
- if (env.CMUX_WORKSPACE_ID || env.CMUX_SURFACE_ID || env.CMUX_REMOTE_TRANSPORT) return true;
13
+ /** Terminal multiplexers omp recognizes as owning the screen grid. */
14
+ export type TerminalMultiplexer = "herdr" | "tmux" | "screen" | "zellij" | "cmux" | "wmux";
15
+
16
+ /**
17
+ * Classify which terminal multiplexer owns the current screen grid, or `null`
18
+ * for a direct terminal. Single source of truth for both the render-path gate
19
+ * (`isInsideTerminalMultiplexer`) and the debug snapshot label.
20
+ *
21
+ * TMUX/STY/ZELLIJ, Herdr, and the CMUX/WMUX workspace/surface/remote-transport
22
+ * markers are authoritative session signals. TERM can also survive when those
23
+ * are stripped (`sudo` without -E, `su`, env-sanitizing launchers/ssh). Do not
24
+ * use CMUX_SOCKET_PATH / WMUX_CLI / WMUX_PIPE here: they are CLI socket/path
25
+ * overrides and can be set outside a CMUX/WMUX terminal. wmux is a Windows
26
+ * multiplexer (Electron + xterm.js) modeled on cmux/herdr that repaints its
27
+ * pane in place and exports WMUX=1 plus a native WMUX_SURFACE_ID.
28
+ */
29
+ export function classifyTerminalMultiplexer(env: NodeJS.ProcessEnv = Bun.env): TerminalMultiplexer | null {
30
+ if (isInsideHerdr(env)) return "herdr";
31
+ if (env.TMUX) return "tmux";
32
+ if (env.STY) return "screen";
33
+ if (env.ZELLIJ) return "zellij";
34
+ if (env.CMUX_WORKSPACE_ID || env.CMUX_SURFACE_ID || env.CMUX_REMOTE_TRANSPORT) return "cmux";
35
+ if (env.WMUX === "1" || env.WMUX_SURFACE_ID) return "wmux";
22
36
  const term = env.TERM?.toLowerCase() ?? "";
23
- return term.startsWith("tmux") || term.startsWith("screen");
37
+ if (term.startsWith("tmux")) return "tmux";
38
+ if (term.startsWith("screen")) return "screen";
39
+ return null;
40
+ }
41
+
42
+ /** True when a terminal multiplexer owns the current screen grid. */
43
+ export function isInsideTerminalMultiplexer(env: NodeJS.ProcessEnv = Bun.env): boolean {
44
+ return classifyTerminalMultiplexer(env) !== null;
24
45
  }
package/src/terminal.ts CHANGED
@@ -1,16 +1,10 @@
1
1
  import { dlopen, FFIType, ptr } from "bun:ffi";
2
2
  import * as fs from "node:fs";
3
3
  import { TtyWriter } from "@oh-my-pi/pi-natives";
4
- import {
5
- $env,
6
- isBunTestRuntime,
7
- isTerminalHeadless,
8
- isWsl,
9
- logger,
10
- postmortem,
11
- restoreTerminalStderr,
12
- suppressTerminalStderr,
13
- } from "@oh-my-pi/pi-utils";
4
+ import { $env, isBunTestRuntime, isTerminalHeadless, isWsl } from "@oh-my-pi/pi-utils/env";
5
+ import * as logger from "@oh-my-pi/pi-utils/logger";
6
+ import * as postmortem from "@oh-my-pi/pi-utils/postmortem";
7
+ import { restoreTerminalStderr, suppressTerminalStderr } from "@oh-my-pi/pi-utils/stderr-guard";
14
8
  import { setKittyProtocolActive } from "./keys";
15
9
  import { StdinBuffer } from "./stdin-buffer";
16
10
  import {
@@ -520,6 +514,20 @@ export interface Terminal {
520
514
  */
521
515
  readonly pendingOutputBytes?: number;
522
516
 
517
+ /**
518
+ * Whether a pseudoconsole host owns the grid this terminal writes to, so
519
+ * neither the cursor nor the painted rows survive a resize under the
520
+ * application's own model. Measured on Windows conhost: resizing the
521
+ * pseudoconsole makes it re-emit its whole viewport from `CSI H` with
522
+ * absolute addressing while the application writes nothing, and it re-homes
523
+ * the cursor, so a DSR reply after a resize reports column 1 instead of the
524
+ * column the application parked. The renderer's resize anchor recovery needs
525
+ * both properties, so it takes the rebuild path instead when this is set.
526
+ * Optional so custom Terminals built against older pi-tui versions keep
527
+ * working; absent means the terminal itself owns the grid.
528
+ */
529
+ readonly hostOwnsGridOnResize?: boolean;
530
+
523
531
  // Whether Kitty keyboard protocol is active
524
532
  get kittyProtocolActive(): boolean;
525
533
 
@@ -662,8 +670,9 @@ function isPrivateModeSupported(status: string): boolean {
662
670
  export interface ProcessTerminalOptions {
663
671
  /**
664
672
  * Force ConPTY-hosted behavior on or off. Defaults to live detection via
665
- * {@link isConPTYHosted}. Tests set this so the kitty-flag and write-chunking
666
- * paths stay hermetic regardless of the ambient WSL env (`WSL_DISTRO_NAME` /
673
+ * {@link isConPTYHosted}. Tests set this so the kitty-flag, write-chunking
674
+ * and resize-routing ({@link Terminal.hostOwnsGridOnResize}) paths stay
675
+ * hermetic regardless of the ambient WSL env (`WSL_DISTRO_NAME` /
667
676
  * `WSL_INTEROP`) — the suite must behave identically on WSL and on CI.
668
677
  */
669
678
  conpty?: boolean;
@@ -1995,6 +2004,14 @@ export class ProcessTerminal implements Terminal {
1995
2004
  return process.stdout.writableLength ?? 0;
1996
2005
  }
1997
2006
 
2007
+ get hostOwnsGridOnResize(): boolean {
2008
+ // #conpty, not a fresh isConPTYHosted() call: the construction override
2009
+ // must gate every ConPTY-dependent path uniformly, or an injected
2010
+ // `conpty` value models one host for writes and kitty flags and the
2011
+ // opposite host for resize routing.
2012
+ return this.#conpty;
2013
+ }
2014
+
1998
2015
  /**
1999
2016
  * Reconcile the stdout backlog after a write or a poll. The watchdog runs an
2000
2017
  * episode from the moment the backlog crosses the arm cap until it drains to