@tuidom/terminal-backend 0.9.1 → 0.11.0

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.
@@ -3,6 +3,8 @@ import { Point, Size } from "@tuidom/core/common/geometryPromitives";
3
3
  import type { KeyPressEvent } from "@tuidom/core/input/keyEvent";
4
4
  import type { MouseToken } from "@tuidom/core/input/rawTerminalToken";
5
5
  import { Grid } from "@tuidom/core/rendering/grid";
6
+ /** OSC 0 — icon name and window title at once; BEL is the terminator everyone understands. */
7
+ export declare function windowTitleSequence(title: string): string;
6
8
  /**
7
9
  * Real terminal backend: reads from process.stdin, writes to process.stdout.
8
10
  * Handles alternate screen, raw mode, cursor visibility, signal cleanup.
@@ -29,6 +31,8 @@ export declare class NodeTerminalBackend implements ITerminalBackend {
29
31
  private lastEmittedSize;
30
32
  private resizePending;
31
33
  private cleanupHandlers;
34
+ /** The title stack got a push from {@link setWindowTitle}; teardown owes it a pop. */
35
+ private titlePushed;
32
36
  private readonly isTmux;
33
37
  private readonly isGnuScreen;
34
38
  private readonly inputParser;
@@ -67,6 +71,14 @@ export declare class NodeTerminalBackend implements ITerminalBackend {
67
71
  * TMUX passthrough is applied automatically when running inside TMUX.
68
72
  */
69
73
  writeOscSequence(sequence: string): void;
74
+ /**
75
+ * Set the terminal window title (OSC 0). Written directly, never through tmux
76
+ * passthrough: tmux understands OSC 0/2 itself and keeps it as the pane title
77
+ * (`#{pane_title}`, `#T`), from where `set-titles` / `automatic-rename-format` carry it
78
+ * to the outer window and the tmux window name. The first call saves the current title
79
+ * on the terminal's title stack; {@link teardown} restores it.
80
+ */
81
+ setWindowTitle(title: string): void;
70
82
  onInput(callback: (event: KeyPressEvent) => void): void;
71
83
  onMouse(callback: (event: MouseToken) => void): void;
72
84
  onPaste(callback: (text: string) => void): void;
@@ -38,6 +38,19 @@ const MODIFY_OTHER_KEYS_DISABLE = "\x1b[>4;0m";
38
38
  */
39
39
  const BRACKETED_PASTE_ENABLE = "\x1b[?2004h";
40
40
  const BRACKETED_PASTE_DISABLE = "\x1b[?2004l";
41
+ /**
42
+ * Window-title stack (XTWINOPS): `CSI 22 ; 0 t` saves the icon and window titles,
43
+ * `CSI 23 ; 0 t` restores them — the same push/pop vim does around its own title, so the
44
+ * shell's title comes back on exit. tmux keeps the stack per pane; terminals without it
45
+ * ignore the sequence.
46
+ */
47
+ const TITLE_PUSH = "\x1b[22;0t";
48
+ const TITLE_POP = "\x1b[23;0t";
49
+ /** OSC 0 — icon name and window title at once; BEL is the terminator everyone understands. */
50
+ export function windowTitleSequence(title) {
51
+ // A control character inside the title would cut the sequence short.
52
+ return `\x1b]0;${title.replace(/[\x00-\x1f\x7f]/g, " ")}\x07`;
53
+ }
41
54
  /**
42
55
  * Keyboard-protocol probe: query current Kitty flags (`CSI ? u`) immediately followed by
43
56
  * Primary Device Attributes (`CSI c`). Only Kitty-capable terminals answer the first; every
@@ -138,6 +151,8 @@ export class NodeTerminalBackend {
138
151
  lastEmittedSize = null;
139
152
  resizePending = false;
140
153
  cleanupHandlers = [];
154
+ /** The title stack got a push from {@link setWindowTitle}; teardown owes it a pop. */
155
+ titlePushed = false;
141
156
  isTmux;
142
157
  isGnuScreen;
143
158
  inputParser = new KeyInputParser();
@@ -187,6 +202,20 @@ export class NodeTerminalBackend {
187
202
  writeOscSequence(sequence) {
188
203
  this.writePassthrough(sequence);
189
204
  }
205
+ /**
206
+ * Set the terminal window title (OSC 0). Written directly, never through tmux
207
+ * passthrough: tmux understands OSC 0/2 itself and keeps it as the pane title
208
+ * (`#{pane_title}`, `#T`), from where `set-titles` / `automatic-rename-format` carry it
209
+ * to the outer window and the tmux window name. The first call saves the current title
210
+ * on the terminal's title stack; {@link teardown} restores it.
211
+ */
212
+ setWindowTitle(title) {
213
+ if (!this.titlePushed) {
214
+ this.writeDirect(TITLE_PUSH);
215
+ this.titlePushed = true;
216
+ }
217
+ this.writeDirect(windowTitleSequence(title));
218
+ }
190
219
  onInput(callback) {
191
220
  this.inputCallbacks.push(callback);
192
221
  }
@@ -460,6 +489,11 @@ export class NodeTerminalBackend {
460
489
  this.stdout.write("\x1b[?25h");
461
490
  // Restore normal screen buffer
462
491
  this.stdout.write("\x1b[?1049l");
492
+ // Restore the title saved by the first setWindowTitle — direct, like the title itself
493
+ if (this.titlePushed) {
494
+ this.writeDirect(TITLE_POP);
495
+ this.titlePushed = false;
496
+ }
463
497
  // Remove stdin listener
464
498
  if (this.onDataHandler) {
465
499
  this.stdin.removeListener("data", this.onDataHandler);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuidom/terminal-backend",
3
- "version": "0.9.1",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "description": "Experimental terminal UI DOM — real terminal backend for Node (tty, ANSI diffing, Kitty protocol). API is unstable.",
6
6
  "license": "MIT",
@@ -28,6 +28,6 @@
28
28
  "./*": "./dist/*.js"
29
29
  },
30
30
  "dependencies": {
31
- "@tuidom/core": "0.9.1"
31
+ "@tuidom/core": "0.11.0"
32
32
  }
33
33
  }