@gajae-code/tui 0.7.10 → 0.7.11

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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.7.11] - 2026-07-03
6
+
7
+ ### Fixed
8
+
9
+ - Kept the terminal stdout error handler armed briefly after TUI shutdown so late `EIO`/closed-PTY errors from SSH or Windows Terminal detach do not crash tmux-backed GJC panes.
10
+
5
11
  ## [0.7.9] - 2026-07-01
6
12
 
7
13
  ### Fixed
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/tui",
4
- "version": "0.7.10",
4
+ "version": "0.7.11",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://gaebal-gajae.dev",
7
7
  "author": "Yeachan-Heo",
@@ -38,8 +38,8 @@
38
38
  "fmt": "biome format --write ."
39
39
  },
40
40
  "dependencies": {
41
- "@gajae-code/natives": "0.7.10",
42
- "@gajae-code/utils": "0.7.10",
41
+ "@gajae-code/natives": "0.7.11",
42
+ "@gajae-code/utils": "0.7.11",
43
43
  "lru-cache": "11.3.6",
44
44
  "marked": "^18.0.3"
45
45
  },
@@ -58,7 +58,8 @@ export class Input implements Component, Focusable {
58
58
  setValue(value: string): void {
59
59
  const normalized = value.normalize("NFC");
60
60
  this.#value = normalized;
61
- this.#cursor = Math.min(this.#cursor, normalized.length);
61
+ this.#cursor = normalized.length;
62
+ this.#lastAction = null;
62
63
  }
63
64
 
64
65
  handleInput(data: string): void {
package/src/terminal.ts CHANGED
@@ -167,6 +167,7 @@ export function resolveTerminalRows(
167
167
  function isWindowsSubsystemForLinux(): boolean {
168
168
  return process.platform === "linux" && (!!$env.WSL_DISTRO_NAME || !!$env.WSL_INTEROP);
169
169
  }
170
+ const STDOUT_ERROR_HANDLER_GRACE_MS = 250;
170
171
 
171
172
  /**
172
173
  * Real terminal using process.stdin/stdout
@@ -185,6 +186,7 @@ export class ProcessTerminal implements Terminal {
185
186
  #detachLogPath = $env.PI_TUI_TERMINAL_DETACH_LOG || "";
186
187
  #windowsVTInputRestore?: () => void;
187
188
  #stdoutErrorHandler?: (err: Error) => void;
189
+ #stdoutErrorHandlerCleanupTimer?: Timer;
188
190
  #appearanceCallbacks: Array<(appearance: TerminalAppearance) => void> = [];
189
191
  #appearance: TerminalAppearance | undefined;
190
192
  #osc11Pending = false;
@@ -233,10 +235,16 @@ export class ProcessTerminal implements Terminal {
233
235
 
234
236
  // Set up resize handler immediately
235
237
  process.stdout.on("resize", this.#resizeHandler);
236
- this.#stdoutErrorHandler = (err: Error) => {
237
- this.#markUnavailable(err, "stdout-error");
238
- };
239
- process.stdout.on("error", this.#stdoutErrorHandler);
238
+ if (this.#stdoutErrorHandlerCleanupTimer) {
239
+ clearTimeout(this.#stdoutErrorHandlerCleanupTimer);
240
+ this.#stdoutErrorHandlerCleanupTimer = undefined;
241
+ }
242
+ if (!this.#stdoutErrorHandler) {
243
+ this.#stdoutErrorHandler = (err: Error) => {
244
+ this.#markUnavailable(err, "stdout-error");
245
+ };
246
+ process.stdout.on("error", this.#stdoutErrorHandler);
247
+ }
240
248
 
241
249
  // Refresh terminal dimensions - they may be stale after suspend/resume
242
250
  // (SIGWINCH is lost while process is stopped). Unix only.
@@ -692,10 +700,7 @@ export class ProcessTerminal implements Terminal {
692
700
  process.stdout.removeListener("resize", this.#resizeHandler);
693
701
  this.#resizeHandler = undefined;
694
702
  }
695
- if (this.#stdoutErrorHandler) {
696
- process.stdout.removeListener("error", this.#stdoutErrorHandler);
697
- this.#stdoutErrorHandler = undefined;
698
- }
703
+ this.#scheduleStdoutErrorHandlerCleanup();
699
704
 
700
705
  // Pause stdin to prevent any buffered input (e.g., Ctrl+D) from being
701
706
  // re-interpreted after raw mode is disabled. This fixes a race condition
@@ -708,6 +713,23 @@ export class ProcessTerminal implements Terminal {
708
713
  }
709
714
  }
710
715
 
716
+ #scheduleStdoutErrorHandlerCleanup(): void {
717
+ if (!this.#stdoutErrorHandler) return;
718
+ if (this.#stdoutErrorHandlerCleanupTimer) clearTimeout(this.#stdoutErrorHandlerCleanupTimer);
719
+ // Terminal restore writes above can fail asynchronously after stop() returns
720
+ // when an SSH/Windows Terminal PTY disappears. Keep the stdout error listener
721
+ // armed briefly so late EIO/EPIPE events mark the terminal unavailable instead
722
+ // of surfacing as uncaught exceptions that kill the tmux pane.
723
+ this.#stdoutErrorHandlerCleanupTimer = setTimeout(() => {
724
+ if (this.#stdoutErrorHandler) {
725
+ process.stdout.removeListener("error", this.#stdoutErrorHandler);
726
+ this.#stdoutErrorHandler = undefined;
727
+ }
728
+ this.#stdoutErrorHandlerCleanupTimer = undefined;
729
+ }, STDOUT_ERROR_HANDLER_GRACE_MS);
730
+ this.#stdoutErrorHandlerCleanupTimer.unref?.();
731
+ }
732
+
711
733
  write(data: string): void {
712
734
  this.#safeWrite(data);
713
735
  if (this.#writeLogPath) {