poly-weaver 0.10.6 → 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.
Files changed (114) hide show
  1. package/README.md +16 -24
  2. package/dist/agents/runner.d.ts.map +1 -1
  3. package/dist/agents/runner.js +6 -1
  4. package/dist/agents/runner.js.map +1 -1
  5. package/dist/ansi.d.ts +23 -4
  6. package/dist/ansi.d.ts.map +1 -1
  7. package/dist/ansi.js +28 -5
  8. package/dist/ansi.js.map +1 -1
  9. package/dist/dump/hotkey.d.ts +0 -3
  10. package/dist/dump/hotkey.d.ts.map +1 -1
  11. package/dist/dump/hotkey.js +0 -3
  12. package/dist/dump/hotkey.js.map +1 -1
  13. package/dist/offscreen/controller.d.ts +4 -1
  14. package/dist/offscreen/controller.d.ts.map +1 -1
  15. package/dist/offscreen/controller.js +48 -3
  16. package/dist/offscreen/controller.js.map +1 -1
  17. package/dist/offscreen/fork-launch.d.ts +1 -0
  18. package/dist/offscreen/fork-launch.d.ts.map +1 -1
  19. package/dist/offscreen/fork-launch.js +4 -0
  20. package/dist/offscreen/fork-launch.js.map +1 -1
  21. package/dist/offscreen/fork.d.ts +6 -1
  22. package/dist/offscreen/fork.d.ts.map +1 -1
  23. package/dist/offscreen/fork.js +26 -3
  24. package/dist/offscreen/fork.js.map +1 -1
  25. package/dist/offscreen/surface.d.ts.map +1 -1
  26. package/dist/offscreen/surface.js +6 -2
  27. package/dist/offscreen/surface.js.map +1 -1
  28. package/dist/providers/claude/strategy.d.ts +6 -0
  29. package/dist/providers/claude/strategy.d.ts.map +1 -1
  30. package/dist/providers/claude/strategy.js +2 -0
  31. package/dist/providers/claude/strategy.js.map +1 -1
  32. package/dist/providers/codex/strategy.d.ts +1 -0
  33. package/dist/providers/codex/strategy.d.ts.map +1 -1
  34. package/dist/providers/codex/strategy.js +7 -1
  35. package/dist/providers/codex/strategy.js.map +1 -1
  36. package/dist/providers/types.d.ts +2 -0
  37. package/dist/providers/types.d.ts.map +1 -1
  38. package/dist/session/process-start-time.d.ts.map +1 -1
  39. package/dist/session/process-start-time.js.map +1 -1
  40. package/dist/status-bar.d.ts +7 -0
  41. package/dist/status-bar.d.ts.map +1 -1
  42. package/dist/status-bar.js +34 -6
  43. package/dist/status-bar.js.map +1 -1
  44. package/dist/terminal/host-input-mode.d.ts +4 -0
  45. package/dist/terminal/host-input-mode.d.ts.map +1 -0
  46. package/dist/terminal/host-input-mode.js +33 -0
  47. package/dist/terminal/host-input-mode.js.map +1 -0
  48. package/dist/terminal/host.d.ts +41 -16
  49. package/dist/terminal/host.d.ts.map +1 -1
  50. package/dist/terminal/host.js +197 -64
  51. package/dist/terminal/host.js.map +1 -1
  52. package/dist/terminal/input-router.d.ts +18 -3
  53. package/dist/terminal/input-router.d.ts.map +1 -1
  54. package/dist/terminal/input-router.js +35 -6
  55. package/dist/terminal/input-router.js.map +1 -1
  56. package/dist/terminal/key-event-source.d.ts +6 -15
  57. package/dist/terminal/key-event-source.d.ts.map +1 -1
  58. package/dist/terminal/key-event-source.js +3 -22
  59. package/dist/terminal/key-event-source.js.map +1 -1
  60. package/dist/terminal/key-to-bytes.d.ts +2 -0
  61. package/dist/terminal/key-to-bytes.d.ts.map +1 -1
  62. package/dist/terminal/key-to-bytes.js +89 -13
  63. package/dist/terminal/key-to-bytes.js.map +1 -1
  64. package/dist/terminal/kitty-negotiator.d.ts +9 -0
  65. package/dist/terminal/kitty-negotiator.d.ts.map +1 -0
  66. package/dist/terminal/kitty-negotiator.js +76 -0
  67. package/dist/terminal/kitty-negotiator.js.map +1 -0
  68. package/dist/terminal/koffi-loader.d.ts +1 -5
  69. package/dist/terminal/koffi-loader.d.ts.map +1 -1
  70. package/dist/terminal/koffi-loader.js.map +1 -1
  71. package/dist/terminal/mouse-mode-tracker.d.ts +36 -0
  72. package/dist/terminal/mouse-mode-tracker.d.ts.map +1 -0
  73. package/dist/terminal/mouse-mode-tracker.js +245 -0
  74. package/dist/terminal/mouse-mode-tracker.js.map +1 -0
  75. package/dist/terminal/osc52.d.ts +16 -0
  76. package/dist/terminal/osc52.d.ts.map +1 -0
  77. package/dist/terminal/osc52.js +18 -0
  78. package/dist/terminal/osc52.js.map +1 -0
  79. package/dist/terminal/render.d.ts +14 -14
  80. package/dist/terminal/render.d.ts.map +1 -1
  81. package/dist/terminal/render.js +20 -28
  82. package/dist/terminal/render.js.map +1 -1
  83. package/dist/terminal/stdin-byte-source.d.ts +4 -5
  84. package/dist/terminal/stdin-byte-source.d.ts.map +1 -1
  85. package/dist/terminal/stdin-byte-source.js +11 -13
  86. package/dist/terminal/stdin-byte-source.js.map +1 -1
  87. package/dist/terminal/virtual-terminal.d.ts +9 -1
  88. package/dist/terminal/virtual-terminal.d.ts.map +1 -1
  89. package/dist/terminal/virtual-terminal.js +33 -1
  90. package/dist/terminal/virtual-terminal.js.map +1 -1
  91. package/dist/terminal-input.d.ts +13 -12
  92. package/dist/terminal-input.d.ts.map +1 -1
  93. package/dist/terminal-input.js +82 -108
  94. package/dist/terminal-input.js.map +1 -1
  95. package/dist/text-editing/emacs-input.d.ts +1 -6
  96. package/dist/text-editing/emacs-input.d.ts.map +1 -1
  97. package/dist/text-editing/emacs-input.js +15 -6
  98. package/dist/text-editing/emacs-input.js.map +1 -1
  99. package/dist/text-editing/readline-ops.d.ts.map +1 -1
  100. package/dist/text-editing/readline-ops.js +11 -2
  101. package/dist/text-editing/readline-ops.js.map +1 -1
  102. package/package.json +4 -1
  103. package/dist/terminal/win32-console-mode.d.ts +0 -23
  104. package/dist/terminal/win32-console-mode.d.ts.map +0 -1
  105. package/dist/terminal/win32-console-mode.js +0 -67
  106. package/dist/terminal/win32-console-mode.js.map +0 -1
  107. package/dist/terminal/win32-console-source.d.ts +0 -26
  108. package/dist/terminal/win32-console-source.d.ts.map +0 -1
  109. package/dist/terminal/win32-console-source.js +0 -277
  110. package/dist/terminal/win32-console-source.js.map +0 -1
  111. package/dist/terminal/win32-key-translator.d.ts +0 -26
  112. package/dist/terminal/win32-key-translator.d.ts.map +0 -1
  113. package/dist/terminal/win32-key-translator.js +0 -109
  114. package/dist/terminal/win32-key-translator.js.map +0 -1
@@ -2,8 +2,9 @@ import { type TerminalLayout } from "./layout.js";
2
2
  import { BufferOrchestrationOutput, type OrchestrationOutput } from "./output.js";
3
3
  import { type ParentUiMode } from "./input-router.js";
4
4
  import { type KeyEventSource } from "./key-event-source.js";
5
+ import { type MouseMode } from "./mouse-mode-tracker.js";
5
6
  import type { PtyHandle } from "../pty/types.js";
6
- import type { StatusBar } from "../status-bar.js";
7
+ import type { StatusBar, StatusChildState } from "../status-bar.js";
7
8
  export interface TerminalHostOptions {
8
9
  stdin?: NodeJS.ReadStream;
9
10
  stdout?: NodeJS.WriteStream;
@@ -83,10 +84,10 @@ export interface HostParentUiMode extends ParentUiMode {
83
84
  * - Routes stdin through global shortcuts (dump hotkey, host scrollback,
84
85
  * host interrupt), then the active parent UI mode (review prompts,
85
86
  * etc.), then the attached child.
86
- * - Enables SGR mouse reporting on the real terminal so wheel events
87
- * drive the host-managed scrollback viewport, and disables it on
88
- * teardown / suspension so foreign UIs and the post-host shell are
89
- * not left with mouse mode turned on.
87
+ * - Keeps host-owned mouse capture active for wheel input in normal-buffer
88
+ * host UI, mirrors mouse-reporting modes requested by child PTYs, and
89
+ * disables reflected modes on teardown / suspension so foreign UIs and
90
+ * the post-host shell are not left with mouse mode turned on.
90
91
  */
91
92
  export declare class TerminalHost {
92
93
  private readonly stdin;
@@ -107,12 +108,15 @@ export declare class TerminalHost {
107
108
  private readonly injectedSource;
108
109
  private sourceUnsubscribe;
109
110
  private sourceResizeUnsubscribe;
111
+ private readonly kittyNegotiator;
112
+ private readonly childMouseTracker;
113
+ private readonly mouseModes;
110
114
  /**
111
115
  * Authoritative terminal size as observed by the host. Updated by both
112
- * source-driven (FFI `WINDOW_BUFFER_SIZE_RECORD`) and stdout-driven
113
- * (libuv `process.stdout.emit("resize")`) resize signals. Read by
114
- * `computeLayout()`. Undefined until the first resize observation, in
115
- * which case `computeLayout()` falls back to `stdout.columns`/`rows`.
116
+ * source-driven and stdout-driven (`process.stdout.emit("resize")`)
117
+ * resize signals. Read by `computeLayout()`. Undefined until the first
118
+ * resize observation, in which case `computeLayout()` falls back to
119
+ * `stdout.columns`/`rows`.
116
120
  */
117
121
  private lastSize;
118
122
  private renderScheduled;
@@ -127,6 +131,10 @@ export declare class TerminalHost {
127
131
  */
128
132
  private pendingFullRender;
129
133
  private parentUi;
134
+ private mainChildStatus;
135
+ private externalChildStatus;
136
+ private syncOutputSupported;
137
+ private lastFrameRows;
130
138
  constructor(opts?: TerminalHostOptions);
131
139
  /** True only when the host has entered the alternate screen. */
132
140
  get isActive(): boolean;
@@ -177,8 +185,21 @@ export declare class TerminalHost {
177
185
  * trailing output.
178
186
  */
179
187
  clearChildSurface(): void;
188
+ setActiveChildStatus(state: StatusChildState | undefined): void;
189
+ setMouseModeSource(source: string, modes: Iterable<MouseMode>): void;
190
+ clearMouseModeSource(source: string): void;
180
191
  /** Dispose the current `VirtualTerminal` and create a fresh one sized to the current layout. */
181
192
  private resetVirtualTerminal;
193
+ /**
194
+ * Reset the per-child negotiation trackers (kitty keyboard + mouse modes)
195
+ * and re-derive the host's mouse capture from the now-empty child mode set.
196
+ * Shared by child attach / detach / surface-clear.
197
+ */
198
+ private resetChildNegotiationState;
199
+ private handleOscClipboard;
200
+ private reflectMainChildMouseModes;
201
+ private reconcileHostMouseCapture;
202
+ private writeMouseDelta;
182
203
  /**
183
204
  * Attach a parent UI mode. The host routes stdin through the mode's
184
205
  * `onInput` and queries `getSurface()` (when present) on every frame.
@@ -191,8 +212,8 @@ export declare class TerminalHost {
191
212
  *
192
213
  * The host:
193
214
  * - leaves its own alternate screen,
194
- * - disables mouse reporting so the foreign UI is not flooded with
195
- * SGR mouse sequences,
215
+ * - disables reflected child mouse reporting so the foreign UI is not
216
+ * flooded with mouse sequences,
196
217
  * - removes its stdin data listener,
197
218
  * - restores the prior raw-mode state,
198
219
  * - blocks `scheduleRender()`/`renderNow()` until resume,
@@ -200,14 +221,16 @@ export declare class TerminalHost {
200
221
  * intact so any data buffered during suspension is preserved for
201
222
  * repaint after resume.
202
223
  *
203
- * On resume the host re-enters its alt screen, re-enables mouse
204
- * reporting, re-installs its raw-mode stdin listener, recomputes
205
- * layout, resizes the attached child if any, and schedules a repaint.
224
+ * On resume the host re-enters its alt screen, re-applies still-active
225
+ * child-requested mouse modes, re-installs its raw-mode stdin listener,
226
+ * recomputes layout, resizes the attached child if any, and schedules a
227
+ * repaint.
206
228
  *
207
229
  * Throws when the host is not active. The `finally` clause always
208
230
  * restores host ownership, even when `run` throws.
209
231
  */
210
232
  runIsolatedAltScreen<T>(run: (io: IsolatedAltScreenIo) => Promise<T>): Promise<T>;
233
+ private invalidateFrameCache;
211
234
  /** Force an immediate render (synchronously composes one frame). */
212
235
  renderNow(): void;
213
236
  /**
@@ -237,8 +260,8 @@ export declare class TerminalHost {
237
260
  private handleScrollPages;
238
261
  private handleScrollToTop;
239
262
  private handleScrollToBottom;
240
- private renderUiSurface;
241
- private renderMessages;
263
+ private renderUiSurfaceRows;
264
+ private renderMessageRows;
242
265
  private handleResize;
243
266
  private notifyParentUiResize;
244
267
  private computeLayout;
@@ -249,6 +272,8 @@ export declare class TerminalHost {
249
272
  * before scheduling a coalesced repaint.
250
273
  */
251
274
  private handleOutputChange;
275
+ private publishChildStatus;
276
+ private updateChildStatus;
252
277
  }
253
278
  /**
254
279
  * Map a surface-relative cursor position to a 1-based screen position.
@@ -1 +1 @@
1
- {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/terminal/host.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/terminal/host.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,16 @@
1
- import { ALT_SCREEN_OFF, ALT_SCREEN_ON, CLEAR_SCREEN, CURSOR_HOME, HIDE_CURSOR, MOUSE_OFF, MOUSE_ON, RESET_SCROLL_REGION, SHOW_CURSOR, padOrClip, } from "../ansi.js";
1
+ import { ALT_SCREEN_OFF, ALT_SCREEN_ON, BEGIN_SYNC_UPDATE, CLEAR_SCREEN, CURSOR_HOME, END_SYNC_UPDATE, HIDE_CURSOR, MOUSE_OFF, QUERY_SYNC_UPDATE, RESET_SCROLL_REGION, SHOW_CURSOR, padOrClip, } from "../ansi.js";
2
2
  import { computeTerminalLayout } from "./layout.js";
3
3
  import { VirtualTerminal } from "./virtual-terminal.js";
4
- import { renderChildViewport, renderStatusLine } from "./render.js";
4
+ import { renderChildViewportRows, renderStatusRowBody, shouldShowVtCursor, } from "./render.js";
5
5
  import { BufferOrchestrationOutput, formatOrchestrationMessage, } from "./output.js";
6
6
  import { InputRouter } from "./input-router.js";
7
7
  import { createKeyEventSource } from "./key-event-source.js";
8
+ import { ChildKittyNegotiator } from "./kitty-negotiator.js";
9
+ import { MouseModeReconciler, MouseModeTracker, } from "./mouse-mode-tracker.js";
10
+ import { buildOsc52Forward } from "./osc52.js";
11
+ const MAIN_CHILD_MOUSE_SOURCE = "main-child";
12
+ const HOST_MOUSE_SOURCE = "host-owned";
13
+ const HOST_MOUSE_CAPTURE_MODES = [1000, 1006];
8
14
  /**
9
15
  * Parent-owned orchestration terminal host.
10
16
  *
@@ -19,10 +25,10 @@ import { createKeyEventSource } from "./key-event-source.js";
19
25
  * - Routes stdin through global shortcuts (dump hotkey, host scrollback,
20
26
  * host interrupt), then the active parent UI mode (review prompts,
21
27
  * etc.), then the attached child.
22
- * - Enables SGR mouse reporting on the real terminal so wheel events
23
- * drive the host-managed scrollback viewport, and disables it on
24
- * teardown / suspension so foreign UIs and the post-host shell are
25
- * not left with mouse mode turned on.
28
+ * - Keeps host-owned mouse capture active for wheel input in normal-buffer
29
+ * host UI, mirrors mouse-reporting modes requested by child PTYs, and
30
+ * disables reflected modes on teardown / suspension so foreign UIs and
31
+ * the post-host shell are not left with mouse mode turned on.
26
32
  */
27
33
  export class TerminalHost {
28
34
  stdin;
@@ -43,12 +49,15 @@ export class TerminalHost {
43
49
  injectedSource;
44
50
  sourceUnsubscribe;
45
51
  sourceResizeUnsubscribe;
52
+ kittyNegotiator = new ChildKittyNegotiator();
53
+ childMouseTracker = new MouseModeTracker();
54
+ mouseModes = new MouseModeReconciler();
46
55
  /**
47
56
  * Authoritative terminal size as observed by the host. Updated by both
48
- * source-driven (FFI `WINDOW_BUFFER_SIZE_RECORD`) and stdout-driven
49
- * (libuv `process.stdout.emit("resize")`) resize signals. Read by
50
- * `computeLayout()`. Undefined until the first resize observation, in
51
- * which case `computeLayout()` falls back to `stdout.columns`/`rows`.
57
+ * source-driven and stdout-driven (`process.stdout.emit("resize")`)
58
+ * resize signals. Read by `computeLayout()`. Undefined until the first
59
+ * resize observation, in which case `computeLayout()` falls back to
60
+ * `stdout.columns`/`rows`.
52
61
  */
53
62
  lastSize;
54
63
  renderScheduled = false;
@@ -63,6 +72,10 @@ export class TerminalHost {
63
72
  */
64
73
  pendingFullRender = false;
65
74
  parentUi;
75
+ mainChildStatus;
76
+ externalChildStatus;
77
+ syncOutputSupported = false;
78
+ lastFrameRows = null;
66
79
  constructor(opts = {}) {
67
80
  this.stdin = opts.stdin ?? process.stdin;
68
81
  this.stdout = opts.stdout ?? process.stdout;
@@ -84,6 +97,12 @@ export class TerminalHost {
84
97
  onScrollToTop: () => this.handleScrollToTop(),
85
98
  onScrollToBottom: () => this.handleScrollToBottom(),
86
99
  isChildAlternate: () => this.vt?.isAlternate === true,
100
+ isChildMouseActive: () => this.childMouseTracker.hasActiveReportingMode(),
101
+ getChildKittyFlags: () => this.kittyNegotiator.activeFlags,
102
+ getChildBracketedPaste: () => this.vt?.bracketedPaste === true,
103
+ onSyncOutputReport: (supported) => {
104
+ this.syncOutputSupported = supported;
105
+ },
87
106
  });
88
107
  if (this.statusBar) {
89
108
  this.attachStatusBar(this.statusBar);
@@ -144,17 +163,20 @@ export class TerminalHost {
144
163
  this.started = true;
145
164
  this.stopped = false;
146
165
  this.layout = this.computeLayout();
147
- this.vt = new VirtualTerminal(this.layout.childCols, this.layout.childRows);
148
- this.stdout.write(ALT_SCREEN_ON + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR + MOUSE_ON);
166
+ this.resetVirtualTerminal();
167
+ this.stdout.write(ALT_SCREEN_ON + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);
168
+ this.reconcileHostMouseCapture();
149
169
  if (this.stdin.isTTY) {
150
170
  this.startSource();
171
+ this.stdout.write(QUERY_SYNC_UPDATE);
151
172
  }
152
173
  this.resizeListener = () => this.handleResize();
153
174
  this.stdout.on("resize", this.resizeListener);
154
175
  this.scheduleRender();
155
176
  }
156
177
  startSource() {
157
- this.source = this.injectedSource ?? createKeyEventSource({ stdin: this.stdin });
178
+ this.source = this.injectedSource
179
+ ?? createKeyEventSource({ stdin: this.stdin, stdout: this.stdout });
158
180
  this.source.start();
159
181
  this.sourceUnsubscribe = this.source.onEvents((events) => {
160
182
  this.router.handleEvents(events);
@@ -182,6 +204,7 @@ export class TerminalHost {
182
204
  this.router.destroy();
183
205
  this.vt?.dispose();
184
206
  this.vt = undefined;
207
+ this.mouseModes.clearAll();
185
208
  this.stdout.write(MOUSE_OFF + RESET_SCROLL_REGION + CLEAR_SCREEN + CURSOR_HOME + ALT_SCREEN_OFF + SHOW_CURSOR);
186
209
  // Drop any lingering subscribers — once the host has stopped, late
187
210
  // interrupt callbacks would see torn-down state.
@@ -204,9 +227,12 @@ export class TerminalHost {
204
227
  // Each agent run starts with a fresh VT and empty scrollback so prior
205
228
  // child output cannot remain visible or be reached via PageUp.
206
229
  this.resetVirtualTerminal();
230
+ this.resetChildNegotiationState();
207
231
  this.childHandle = handle;
232
+ this.updateChildStatus();
208
233
  this.router.attachChild(handle);
209
234
  const boundHandle = handle;
235
+ let lastChildAlternate = this.vt?.isAlternate === true;
210
236
  handle.onData((data) => {
211
237
  // Skip render writes if a different child has been attached or the
212
238
  // host has detached this child entirely.
@@ -218,6 +244,13 @@ export class TerminalHost {
218
244
  return;
219
245
  if (!this.vt)
220
246
  return;
247
+ const reply = this.kittyNegotiator.processChunk(data);
248
+ if (reply.length > 0)
249
+ boundHandle.write(reply);
250
+ if (this.childMouseTracker.processChunk(data)) {
251
+ this.reflectMainChildMouseModes();
252
+ this.updateChildStatus();
253
+ }
221
254
  // Capture the user's viewport position BEFORE the write. Two cases:
222
255
  //
223
256
  // stickyBottom=true: user is at the tail → keep them there.
@@ -252,6 +285,12 @@ export class TerminalHost {
252
285
  else if (delta > 0)
253
286
  this.vt.scrollForward(delta);
254
287
  }
288
+ const nextChildAlternate = this.vt.isAlternate === true;
289
+ if (nextChildAlternate !== lastChildAlternate) {
290
+ lastChildAlternate = nextChildAlternate;
291
+ this.invalidateFrameCache();
292
+ this.updateChildStatus();
293
+ }
255
294
  this.scheduleRender();
256
295
  });
257
296
  });
@@ -271,6 +310,9 @@ export class TerminalHost {
271
310
  this.childHandle.suppressOutput();
272
311
  this.router.detachChild(this.childHandle);
273
312
  this.childHandle = undefined;
313
+ this.invalidateFrameCache();
314
+ this.resetChildNegotiationState();
315
+ this.updateChildStatus();
274
316
  }
275
317
  /**
276
318
  * Discard the current child surface (VT screen + scrollback) and
@@ -280,15 +322,62 @@ export class TerminalHost {
280
322
  */
281
323
  clearChildSurface() {
282
324
  this.resetVirtualTerminal();
325
+ this.resetChildNegotiationState();
326
+ this.updateChildStatus();
283
327
  this.scheduleRender();
284
328
  }
329
+ setActiveChildStatus(state) {
330
+ this.externalChildStatus = state;
331
+ this.publishChildStatus();
332
+ }
333
+ setMouseModeSource(source, modes) {
334
+ this.writeMouseDelta(this.mouseModes.setSource(source, modes));
335
+ }
336
+ clearMouseModeSource(source) {
337
+ this.writeMouseDelta(this.mouseModes.clearSource(source));
338
+ }
285
339
  /** Dispose the current `VirtualTerminal` and create a fresh one sized to the current layout. */
286
340
  resetVirtualTerminal() {
287
341
  if (!this.layout) {
288
342
  this.layout = this.computeLayout();
289
343
  }
290
344
  this.vt?.dispose();
291
- this.vt = new VirtualTerminal(this.layout.childCols, this.layout.childRows);
345
+ this.vt = new VirtualTerminal(this.layout.childCols, this.layout.childRows, {
346
+ onOscClipboard: (data) => this.handleOscClipboard(data),
347
+ });
348
+ // A fresh VT means the cached frame no longer reflects the screen.
349
+ this.invalidateFrameCache();
350
+ }
351
+ /**
352
+ * Reset the per-child negotiation trackers (kitty keyboard + mouse modes)
353
+ * and re-derive the host's mouse capture from the now-empty child mode set.
354
+ * Shared by child attach / detach / surface-clear.
355
+ */
356
+ resetChildNegotiationState() {
357
+ this.kittyNegotiator.reset();
358
+ this.childMouseTracker.reset();
359
+ this.reflectMainChildMouseModes();
360
+ }
361
+ handleOscClipboard(data) {
362
+ this.stdout.write(buildOsc52Forward(data));
363
+ }
364
+ reflectMainChildMouseModes() {
365
+ this.setMouseModeSource(MAIN_CHILD_MOUSE_SOURCE, this.childMouseTracker.snapshot());
366
+ }
367
+ reconcileHostMouseCapture() {
368
+ const ownsNormalBuffer = this.started
369
+ && !this.stopped
370
+ && this.mainChildStatus?.isAlternate !== true
371
+ && this.externalChildStatus?.isAlternate !== true;
372
+ const sequence = ownsNormalBuffer
373
+ ? this.mouseModes.setSource(HOST_MOUSE_SOURCE, HOST_MOUSE_CAPTURE_MODES)
374
+ : this.mouseModes.clearSource(HOST_MOUSE_SOURCE);
375
+ this.writeMouseDelta(sequence);
376
+ }
377
+ writeMouseDelta(sequence) {
378
+ if (sequence.length === 0 || this.suspended || this.stopped)
379
+ return;
380
+ this.stdout.write(sequence);
292
381
  }
293
382
  /**
294
383
  * Attach a parent UI mode. The host routes stdin through the mode's
@@ -298,6 +387,7 @@ export class TerminalHost {
298
387
  attachParentUi(mode) {
299
388
  this.parentUi = mode;
300
389
  this.router.attachParentUi(mode);
390
+ this.invalidateFrameCache();
301
391
  this.scheduleRender();
302
392
  return () => this.detachParentUi(mode);
303
393
  }
@@ -306,6 +396,7 @@ export class TerminalHost {
306
396
  return;
307
397
  this.router.detachParentUi(this.parentUi);
308
398
  this.parentUi = undefined;
399
+ this.invalidateFrameCache();
309
400
  this.scheduleRender();
310
401
  }
311
402
  /**
@@ -313,8 +404,8 @@ export class TerminalHost {
313
404
  *
314
405
  * The host:
315
406
  * - leaves its own alternate screen,
316
- * - disables mouse reporting so the foreign UI is not flooded with
317
- * SGR mouse sequences,
407
+ * - disables reflected child mouse reporting so the foreign UI is not
408
+ * flooded with mouse sequences,
318
409
  * - removes its stdin data listener,
319
410
  * - restores the prior raw-mode state,
320
411
  * - blocks `scheduleRender()`/`renderNow()` until resume,
@@ -322,9 +413,10 @@ export class TerminalHost {
322
413
  * intact so any data buffered during suspension is preserved for
323
414
  * repaint after resume.
324
415
  *
325
- * On resume the host re-enters its alt screen, re-enables mouse
326
- * reporting, re-installs its raw-mode stdin listener, recomputes
327
- * layout, resizes the attached child if any, and schedules a repaint.
416
+ * On resume the host re-enters its alt screen, re-applies still-active
417
+ * child-requested mouse modes, re-installs its raw-mode stdin listener,
418
+ * recomputes layout, resizes the attached child if any, and schedules a
419
+ * repaint.
328
420
  *
329
421
  * Throws when the host is not active. The `finally` clause always
330
422
  * restores host ownership, even when `run` throws.
@@ -338,6 +430,8 @@ export class TerminalHost {
338
430
  }
339
431
  this.suspended = true;
340
432
  this.stopSource();
433
+ this.reconcileHostMouseCapture();
434
+ this.mouseModes.setReflectionEnabled(false);
341
435
  this.stdout.write(MOUSE_OFF + RESET_SCROLL_REGION + CLEAR_SCREEN + CURSOR_HOME + ALT_SCREEN_OFF + SHOW_CURSOR);
342
436
  const layoutSnapshot = this.layout ?? this.computeLayout();
343
437
  const io = {
@@ -351,7 +445,12 @@ export class TerminalHost {
351
445
  return await run(io);
352
446
  }
353
447
  finally {
354
- this.stdout.write(ALT_SCREEN_ON + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR + MOUSE_ON);
448
+ this.reconcileHostMouseCapture();
449
+ this.stdout.write(ALT_SCREEN_ON
450
+ + CLEAR_SCREEN
451
+ + CURSOR_HOME
452
+ + HIDE_CURSOR
453
+ + this.mouseModes.setReflectionEnabled(true));
355
454
  if (this.stdin.isTTY) {
356
455
  this.startSource();
357
456
  }
@@ -362,10 +461,14 @@ export class TerminalHost {
362
461
  if (this.childHandle) {
363
462
  this.childHandle.resize(this.layout.childCols, this.layout.childRows);
364
463
  }
464
+ this.invalidateFrameCache();
365
465
  this.notifyParentUiResize();
366
466
  this.scheduleRender();
367
467
  }
368
468
  }
469
+ invalidateFrameCache() {
470
+ this.lastFrameRows = null;
471
+ }
369
472
  /** Force an immediate render (synchronously composes one frame). */
370
473
  renderNow() {
371
474
  this.pendingFlush = false;
@@ -385,46 +488,70 @@ export class TerminalHost {
385
488
  const parentOnly = !fullRender
386
489
  && surface !== undefined
387
490
  && surface.lines.length >= this.layout.childRows;
388
- let frame = "";
491
+ const next = new Array(this.layout.rows).fill(null);
389
492
  if (!parentOnly) {
390
- frame += renderChildViewport(this.vt, this.layout);
493
+ const childRows = renderChildViewportRows(this.vt, this.layout);
494
+ for (let i = 0; i < childRows.length; i++) {
495
+ next[i] = childRows[i];
496
+ }
391
497
  }
392
498
  if (surface) {
393
- frame += this.renderUiSurface(this.layout, surface);
499
+ this.renderUiSurfaceRows(next, this.layout, surface);
394
500
  }
395
501
  // Warning strip is host-owned and lives in its own dedicated rows
396
502
  // (between the child viewport and the status bar), so it is rendered
397
503
  // independently of the parent UI surface and the child viewport.
398
- frame += this.renderMessages(this.layout);
399
- if (this.statusBar) {
504
+ this.renderMessageRows(next, this.layout);
505
+ if (this.statusBar && this.layout.statusRow !== undefined) {
400
506
  const text = this.statusBar.render(this.layout.columns);
401
- frame += renderStatusLine(this.layout, text);
507
+ next[this.layout.statusRow - 1] = renderStatusRowBody(this.layout, text);
402
508
  }
403
- // Move the real cursor to a sensible position for terminal-state
404
- // consistency. Host-rendered frames normally end with HIDE_CURSOR:
405
- // the visible blink/flicker the hardware cursor would otherwise
406
- // produce during provider streaming, host-managed warning rows, and
407
- // host-backed prompts is not desired. A surface can opt in to
408
- // SHOW_CURSOR only for frames where a visible hardware cursor is part
409
- // of its contract.
509
+ // Move the real cursor to the active owner of the frame. Host-owned
510
+ // surfaces keep the historic hidden default unless they opt in; child-owned
511
+ // frames mirror the child's own DECTCEM visibility.
410
512
  const surfaceCursorPlacement = surface?.cursor
411
513
  ? mapSurfaceCursorToScreen(this.layout, surface)
412
514
  : undefined;
413
- if (surface?.showCursor && surfaceCursorPlacement) {
414
- frame += SHOW_CURSOR;
415
- frame += `\x1b[${surfaceCursorPlacement.row};${surfaceCursorPlacement.col}H`;
416
- }
417
- else {
515
+ let cursorPlacement = "";
516
+ if (surface) {
517
+ // Position and visibility are independent: emit the move whenever a
518
+ // placement exists, then decide show/hide separately.
418
519
  if (surfaceCursorPlacement) {
419
- frame += `\x1b[${surfaceCursorPlacement.row};${surfaceCursorPlacement.col}H`;
520
+ cursorPlacement += `\x1b[${surfaceCursorPlacement.row};${surfaceCursorPlacement.col}H`;
420
521
  }
421
- else if (!parentOnly && this.layout.statusRow !== undefined) {
522
+ cursorPlacement += surface.showCursor && surfaceCursorPlacement ? SHOW_CURSOR : HIDE_CURSOR;
523
+ }
524
+ else {
525
+ const hasRoom = this.layout.childRows > 0;
526
+ if (hasRoom) {
422
527
  const cy = Math.min(Math.max(0, this.vt.cursorY), this.layout.childRows - 1);
423
528
  const cx = Math.min(Math.max(0, this.vt.cursorX), this.layout.columns - 1);
424
- frame += `\x1b[${cy + 1};${cx + 1}H`;
529
+ cursorPlacement += `\x1b[${cy + 1};${cx + 1}H`;
530
+ }
531
+ const showChildCursor = hasRoom
532
+ && this.childHandle !== undefined
533
+ && shouldShowVtCursor(this.vt);
534
+ cursorPlacement += showChildCursor ? SHOW_CURSOR : HIDE_CURSOR;
535
+ }
536
+ const prev = this.lastFrameRows;
537
+ let diffBody = HIDE_CURSOR;
538
+ for (let i = 0; i < next.length; i++) {
539
+ if (next[i] !== null) {
540
+ if (prev === null || prev[i] !== next[i]) {
541
+ diffBody += `\x1b[${i + 1};1H${next[i]}`;
542
+ }
543
+ }
544
+ else {
545
+ // Nothing painted this row this frame: carry the prior row forward
546
+ // so `next` doubles as the cache for the following frame.
547
+ next[i] = prev?.[i] ?? null;
425
548
  }
426
- frame += HIDE_CURSOR;
427
549
  }
550
+ this.lastFrameRows = next;
551
+ diffBody += cursorPlacement;
552
+ const frame = this.syncOutputSupported
553
+ ? BEGIN_SYNC_UPDATE + diffBody + END_SYNC_UPDATE
554
+ : diffBody;
428
555
  this.stdout.write(frame);
429
556
  }
430
557
  /**
@@ -512,62 +639,53 @@ export class TerminalHost {
512
639
  this.vt.scrollToBottom();
513
640
  this.scheduleRender();
514
641
  }
515
- renderUiSurface(layout, surface) {
642
+ renderUiSurfaceRows(rows, layout, surface) {
516
643
  if (surface.lines.length === 0)
517
- return "";
644
+ return;
518
645
  const maxLines = Math.min(surface.lines.length, layout.childRows);
519
646
  const baseRow = Math.max(0, layout.childRows - maxLines);
520
- let out = "";
521
647
  for (let i = 0; i < maxLines; i++) {
522
648
  const text = surface.lines[surface.lines.length - maxLines + i];
523
649
  const padded = padOrClip(text, layout.columns);
524
- out += `\x1b[${baseRow + i + 1};1H\x1b[2K\x1b[0m${padded}`;
650
+ rows[baseRow + i] = `\x1b[2K\x1b[0m${padded}`;
525
651
  }
526
- return out;
527
652
  }
528
- renderMessages(layout) {
653
+ renderMessageRows(rows, layout) {
529
654
  if (layout.warningRows === 0 || layout.warningStartRow === undefined)
530
- return "";
655
+ return;
531
656
  const messages = this.bufferOutput.snapshot();
532
657
  if (messages.length === 0)
533
- return "";
658
+ return;
534
659
  // Show the newest messages when the buffer has more than the strip
535
660
  // can hold. The strip is sized to `layout.warningRows`, capped at 3
536
661
  // by `computeTerminalLayout`.
537
662
  const slice = messages.slice(-layout.warningRows);
538
- let out = "";
539
663
  for (let i = 0; i < slice.length; i++) {
540
664
  const text = formatOrchestrationMessage(slice[i]);
541
665
  const padded = padOrClip(text, layout.columns);
542
- out += `\x1b[${layout.warningStartRow + i};1H\x1b[2K\x1b[0m${padded}`;
666
+ rows[layout.warningStartRow - 1 + i] = `\x1b[2K\x1b[0m${padded}`;
543
667
  }
544
- return out;
545
668
  }
546
669
  handleResize(size) {
547
670
  if (!this.isActive)
548
671
  return;
549
672
  let next;
550
673
  if (size) {
551
- // FFI path: WINDOW_BUFFER_SIZE_RECORD dims are authoritative because
552
- // libuv never saw the record and `stdout.columns`/`rows` stay stale.
674
+ // Source-provided dimensions are already post-resize and may be
675
+ // fresher than stdout.columns/stdout.rows.
553
676
  next = size;
554
677
  }
555
678
  else {
556
- // Byte path / stdout.on("resize") path: libuv just refreshed
557
- // `stdout.columns`/`rows`, so they are the authoritative dims.
558
- // Overwrite `lastSize` — any stale FFI value from before the last
559
- // `source.stop()` (e.g. during runIsolatedAltScreen suspension)
560
- // would otherwise mask the new size on resume.
679
+ // stdout.on("resize") path: libuv just refreshed stdout columns/rows,
680
+ // so overwrite `lastSize` with those dimensions.
561
681
  const c = this.stdout.columns;
562
682
  const r = this.stdout.rows;
563
683
  if (typeof c === "number" && typeof r === "number") {
564
684
  next = { columns: c, rows: r };
565
685
  }
566
686
  }
567
- // De-dupe redundant resize cascades. On Windows with the FFI source
568
- // both `source.onResize` (from `WINDOW_BUFFER_SIZE_RECORD`) and
569
- // `stdout.on("resize")` fire for the same physical resize; without
570
- // this guard, layout/vt/child resizes and a render would run twice.
687
+ // De-dupe redundant resize cascades from source-driven and stdout-driven
688
+ // notifications for the same physical resize.
571
689
  if (next !== undefined
572
690
  && this.lastSize !== undefined
573
691
  && next.columns === this.lastSize.columns
@@ -576,6 +694,7 @@ export class TerminalHost {
576
694
  }
577
695
  this.lastSize = next;
578
696
  this.layout = this.computeLayout();
697
+ this.invalidateFrameCache();
579
698
  if (this.vt)
580
699
  this.vt.resize(this.layout.childCols, this.layout.childRows);
581
700
  if (this.childHandle) {
@@ -613,12 +732,26 @@ export class TerminalHost {
613
732
  this.layout = next;
614
733
  if (prev !== undefined
615
734
  && (prev.childCols !== next.childCols || prev.childRows !== next.childRows)) {
735
+ this.invalidateFrameCache();
616
736
  this.vt?.resize(next.childCols, next.childRows);
617
737
  this.childHandle?.resize(next.childCols, next.childRows);
618
738
  this.notifyParentUiResize();
619
739
  }
620
740
  this.scheduleRender();
621
741
  }
742
+ publishChildStatus() {
743
+ this.statusBar?.setChildState(this.externalChildStatus ?? this.mainChildStatus);
744
+ this.reconcileHostMouseCapture();
745
+ }
746
+ updateChildStatus() {
747
+ const hasChild = this.childHandle !== undefined;
748
+ const isAlternate = hasChild && this.vt?.isAlternate === true;
749
+ const mouseActive = hasChild && this.childMouseTracker.hasActiveReportingMode();
750
+ this.mainChildStatus = isAlternate || mouseActive
751
+ ? { isAlternate, mouseActive }
752
+ : undefined;
753
+ this.publishChildStatus();
754
+ }
622
755
  }
623
756
  function isHostParentUiMode(mode) {
624
757
  return typeof mode.getSurface === "function";