@oh-my-pi/pi-tui 17.2.8 → 17.2.10

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,22 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.2.10] - 2026-08-06
6
+
7
+ ### Fixed
8
+
9
+ - Fixed a startup crash (EIO error) in multiplexer or SSH sessions when a revoked pty leaves stdin.isTTY active.
10
+ - Fixed prompt autocomplete to support Windows drive-absolute paths (e.g., C:/ or C:\).
11
+ - Fixed desktop notifications in systemd, tmux, or SSH-attached Linux sessions when DBUS_SESSION_BUS_ADDRESS is unset.
12
+ - Fixed an issue where Shift+letter and shifted symbol inputs (such as capital letters, ?, and !) were silently dropped on Windows and WSL terminals using ConPTY (e.g., WezTerm).
13
+
14
+ ## [17.2.9] - 2026-08-05
15
+
16
+ ### Fixed
17
+
18
+ - Fixed table borders (and adjacent cells) inheriting an open inline-code color when a cell's content wraps mid-code-span, by terminating each wrapped cell line's SGR state before the border glyphs ([#7575](https://github.com/can1357/oh-my-pi/issues/7575)).
19
+ - Fixed inline images not rendering under WSL + Windows Terminal: the SIXEL capability probe gated on `process.platform === "win32"`, but WSL reports `linux`, so the probe never ran and images fell back to the text placeholder even on Sixel-capable Windows Terminal. The probe now runs on any ConPTY host (native win32 or WSL) ([#6009](https://github.com/can1357/oh-my-pi/issues/6009)).
20
+
5
21
  ## [17.2.5] - 2026-08-03
6
22
 
7
23
  ### Fixed
package/README.md CHANGED
@@ -640,7 +640,7 @@ class MyComponent implements Component {
640
640
  - `wrapTextWithAnsi()` preserves ANSI codes while word-wrapping and trimming line ends
641
641
 
642
642
  ```typescript
643
- import chalk from "chalk";
643
+ import chalk from "@oh-my-pi/pi-utils/chalk";
644
644
 
645
645
  const styled = chalk.red("Hello") + " " + chalk.blue("World");
646
646
  const width = visibleWidth(styled); // 11 (not counting ANSI codes)
@@ -7,10 +7,11 @@ export interface DesktopNotifier {
7
7
  }
8
8
  /**
9
9
  * Whether the current process can reach a freedesktop notification daemon:
10
- * Linux platform + a session bus address in env. Caller is still responsible
11
- * for resolving a delivery binary via {@link resolveDesktopNotifier}.
10
+ * Linux platform plus either a session bus address in env or the
11
+ * systemd user-bus socket at `$XDG_RUNTIME_DIR/bus`. Caller is still responsible for
12
+ * resolving a delivery binary via {@link resolveDesktopNotifier}.
12
13
  */
13
- export declare function hasLinuxDesktopSession(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): boolean;
14
+ export declare function hasLinuxDesktopSession(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv, fileExists?: (path: string) => boolean): boolean;
14
15
  /**
15
16
  * Whether `sendNotification` should also dispatch a D-Bus toast for this
16
17
  * terminal. Returns true only when (1) the chosen `notifyProtocol` is BEL,
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.8",
4
+ "version": "17.2.10",
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,13 +37,10 @@
37
37
  "fmt": "biome format --write ."
38
38
  },
39
39
  "dependencies": {
40
- "@oh-my-pi/pi-natives": "17.2.8",
41
- "@oh-my-pi/pi-utils": "17.2.8",
42
- "lru-cache": "11.5.2",
43
- "marked": "^18.0.6"
40
+ "@oh-my-pi/pi-natives": "17.2.10",
41
+ "@oh-my-pi/pi-utils": "17.2.10"
44
42
  },
45
43
  "devDependencies": {
46
- "chalk": "^5.6.2",
47
44
  "ghostty-web": "^0.4.0"
48
45
  },
49
46
  "engines": {
@@ -687,7 +687,9 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
687
687
  pathPrefix.startsWith("/") ||
688
688
  pathPrefix.startsWith("./") ||
689
689
  pathPrefix.startsWith("../") ||
690
- pathPrefix.startsWith("~/")
690
+ pathPrefix.startsWith("~/") ||
691
+ // Windows drive-absolute paths (C:/Users, C:\Users).
692
+ /^[A-Za-z]:[\\/]/.test(pathPrefix)
691
693
  ) {
692
694
  return pathPrefix;
693
695
  }
@@ -34,7 +34,7 @@ export class CancellableLoader extends Loader {
34
34
  }
35
35
  }
36
36
 
37
- dispose(): void {
37
+ override dispose(): void {
38
38
  this.stop();
39
39
  }
40
40
  }
@@ -55,7 +55,7 @@ export class Loader extends Text {
55
55
  this.start();
56
56
  }
57
57
 
58
- render(width: number): readonly string[] {
58
+ override render(width: number): readonly string[] {
59
59
  const source = super.render(width);
60
60
  if (source !== this.#layoutSource) {
61
61
  const paddingX = getPaddingX(1);
@@ -1,5 +1,12 @@
1
- import { LRUCache } from "lru-cache/raw";
2
- import { Lexer, Marked, type Token, Tokenizer, type TokenizerAndRendererExtension, type Tokens } from "marked";
1
+ import { LRUCache } from "@oh-my-pi/pi-utils/lru";
2
+ import {
3
+ Lexer,
4
+ Marked,
5
+ type Token,
6
+ Tokenizer,
7
+ type TokenizerAndRendererExtension,
8
+ type Tokens,
9
+ } from "@oh-my-pi/pi-utils/marked";
3
10
  import { latexToBlock } from "../latex-block";
4
11
  import { inlineMathSpanEnd, isBareMathEnvironment, latexToUnicode } from "../latex-to-unicode";
5
12
  import type { SymbolTheme } from "../symbols";
@@ -804,11 +811,11 @@ markdownParser.use({
804
811
  // (no `m` flag), and stickiness only removes the futile later attempts. The
805
812
  // flags/anchor guard below skips any rule a future marked version changes.
806
813
  class AnchoredAtZero extends RegExp {
807
- exec(str: string): RegExpExecArray | null {
814
+ override exec(str: string): RegExpExecArray | null {
808
815
  this.lastIndex = 0; // sticky matches set lastIndex; rules are shared
809
816
  return super.exec(str);
810
817
  }
811
- test(str: string): boolean {
818
+ override test(str: string): boolean {
812
819
  this.lastIndex = 0;
813
820
  return super.test(str);
814
821
  }
@@ -2816,7 +2823,13 @@ export class Markdown implements Component, NativeScrollbackCommittedRows, Nativ
2816
2823
  while (wrapped.length > 1 && wrapped[wrapped.length - 1] === "") {
2817
2824
  wrapped.pop();
2818
2825
  }
2819
- return wrapped;
2826
+ // The native wrap deliberately leaves fg color and bold/italic open at
2827
+ // line ends so continuation lines can re-open them. Table rows splice
2828
+ // every cell line between unstyled border glyphs, so an open style
2829
+ // (e.g. mdCode) would bleed into the "│" and the following cells.
2830
+ // Terminate each line at default fg, clearing bold/italic but keeping
2831
+ // any ambient background (message-bg rendering) intact.
2832
+ return wrapped.map(line => `${line}\x1b[22m\x1b[23m\x1b[39m`);
2820
2833
  }
2821
2834
 
2822
2835
  /**
@@ -20,6 +20,8 @@
20
20
  // iTerm2, WezTerm, …) keep working unchanged and the BEL emission still fires
21
21
  // for tmux `monitor-bell`, X11 urgency hints, and audible-bell handlers.
22
22
 
23
+ import * as fs from "node:fs";
24
+ import * as path from "node:path";
23
25
  import { $which } from "@oh-my-pi/pi-utils";
24
26
  import type { TerminalId, TerminalNotification } from "./terminal-capabilities";
25
27
 
@@ -36,15 +38,19 @@ export interface DesktopNotifier {
36
38
 
37
39
  /**
38
40
  * Whether the current process can reach a freedesktop notification daemon:
39
- * Linux platform + a session bus address in env. Caller is still responsible
40
- * for resolving a delivery binary via {@link resolveDesktopNotifier}.
41
+ * Linux platform plus either a session bus address in env or the
42
+ * systemd user-bus socket at `$XDG_RUNTIME_DIR/bus`. Caller is still responsible for
43
+ * resolving a delivery binary via {@link resolveDesktopNotifier}.
41
44
  */
42
45
  export function hasLinuxDesktopSession(
43
46
  platform: NodeJS.Platform = process.platform,
44
47
  env: NodeJS.ProcessEnv = Bun.env,
48
+ fileExists: (path: string) => boolean = fs.existsSync,
45
49
  ): boolean {
46
50
  if (platform !== "linux") return false;
47
- return Boolean(env.DBUS_SESSION_BUS_ADDRESS);
51
+ if (env.DBUS_SESSION_BUS_ADDRESS) return true;
52
+ const runtimeDir = env.XDG_RUNTIME_DIR;
53
+ return Boolean(runtimeDir && fileExists(path.join(runtimeDir, "bus")));
48
54
  }
49
55
 
50
56
  /**
package/src/terminal.ts CHANGED
@@ -717,10 +717,17 @@ export class ProcessTerminal implements Terminal {
717
717
  // stderr-guard in pi-utils (mirrors openai/codex#24459).
718
718
  suppressTerminalStderr();
719
719
 
720
- // Save previous state and enable raw mode
720
+ // A multiplexer or SSH disconnect can leave isTTY true after its pty has
721
+ // been revoked. Raw mode is then impossible, so take the normal terminal
722
+ // disconnect path rather than letting Bun abort startup with EIO.
721
723
  this.#wasRaw = process.stdin.isRaw || false;
722
724
  if (process.stdin.setRawMode) {
723
- process.stdin.setRawMode(true);
725
+ try {
726
+ process.stdin.setRawMode(true);
727
+ } catch (err) {
728
+ this.#markTerminalDisconnected("stdin raw mode setup failed", err);
729
+ return;
730
+ }
724
731
  }
725
732
  process.stdin.setEncoding("utf8");
726
733
  process.stdin.on("end", this.#stdinEndHandler);
@@ -1100,7 +1107,13 @@ export class ProcessTerminal implements Terminal {
1100
1107
  const reportedFlags = parseInt(match[1]!, 10);
1101
1108
  this.#kittyProtocolActive = true;
1102
1109
  setKittyProtocolActive(true);
1103
- if ((reportedFlags & 2) !== 0) {
1110
+ if (isConPTYHosted()) {
1111
+ // ConPTY (native Windows and WSL) drops Shift+letter keypresses
1112
+ // entirely when flag 4 (report alternate keys) is set. Use flag 1
1113
+ // (disambiguate only), preserving flag 2 if already active.
1114
+ this.#kittyEnableSeq = (reportedFlags & 2) !== 0 ? "\x1b[>3u" : "\x1b[>1u";
1115
+ this.#safeWrite(this.#kittyEnableSeq);
1116
+ } else if ((reportedFlags & 2) !== 0) {
1104
1117
  // Preserve event-type reporting already enabled by a parent app.
1105
1118
  // Push level-2 to keep its shortcuts reporting consistently.
1106
1119
  this.#kittyEnableSeq = "\x1b[>7u";
package/src/tui.ts CHANGED
@@ -1670,7 +1670,10 @@ export class TUI extends Container {
1670
1670
 
1671
1671
  #querySixelSupport(): void {
1672
1672
  if (TERMINAL.imageProtocol) return;
1673
- if (process.platform !== "win32") return;
1673
+ // win32 native or WSL under Windows Terminal — both are ConPTY-hosted and
1674
+ // reach the same WT graphics negotiation. WSL reports process.platform
1675
+ // "linux", so a bare win32 check silently skips the probe there (#6009).
1676
+ if (!isConPTYHosted()) return;
1674
1677
  if (!Bun.env.WT_SESSION) return;
1675
1678
  if (!process.stdin.isTTY || !process.stdout.isTTY) return;
1676
1679