tmux-ide 2.9.12 → 2.9.13

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 (48) hide show
  1. package/bin/cli.js +3438 -2062
  2. package/package.json +1 -1
  3. package/packages/contracts/src/index.ts +2 -0
  4. package/packages/contracts/src/pane-stream.ts +17 -0
  5. package/packages/contracts/src/terminal-delivery.ts +2 -1
  6. package/packages/contracts/src/terminal-graphics-compressed.ts +66 -0
  7. package/packages/contracts/src/terminal-graphics.ts +5 -0
  8. package/packages/contracts/src/terminal-surfaces.ts +137 -0
  9. package/packages/core/src/terminal-delivery.ts +8 -2
  10. package/packages/core/src/terminal-replica.ts +33 -0
  11. package/packages/daemon/dist/command-center/resources/saved-machine-route.js +2 -0
  12. package/packages/daemon/dist/command-center/resources/saved-machine-watch.js +117 -0
  13. package/packages/daemon/dist/command-center/server.js +13 -0
  14. package/packages/daemon/dist/command-center/tmux-servers.js +9 -0
  15. package/packages/daemon/dist/lib/daemon-embed.js +32 -1
  16. package/packages/daemon/dist/lib/native-surface-context.js +96 -0
  17. package/packages/daemon/dist/lib/tmux-server-owner.js +9 -0
  18. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
  19. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
  20. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
  21. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +2 -2
  22. package/packages/daemon/dist/server/native-surface-provider-upgrade.js +88 -0
  23. package/packages/daemon/dist/terminal/graphics/compress-chunk.js +40 -0
  24. package/packages/daemon/dist/terminal/graphics/native-surface-demand.js +99 -0
  25. package/packages/daemon/dist/terminal/graphics/native-surface-provider-channel.js +162 -0
  26. package/packages/daemon/dist/terminal/graphics/native-surface-provider.js +275 -0
  27. package/packages/daemon/dist/terminal/graphics/native-surface-viewer.js +146 -0
  28. package/packages/daemon/dist/terminal/mirror/mirror-service.js +3 -0
  29. package/packages/daemon/dist/terminal/mirror/session-channel.js +32 -9
  30. package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +196 -3
  31. package/packages/daemon/dist/terminal/pane-stream/runtime.js +9 -0
  32. package/packages/daemon/src/command-center/resources/saved-machine-route.ts +4 -0
  33. package/packages/daemon/src/command-center/resources/saved-machine-watch.ts +132 -0
  34. package/packages/daemon/src/command-center/server.ts +18 -0
  35. package/packages/daemon/src/command-center/tmux-servers.ts +14 -0
  36. package/packages/daemon/src/lib/daemon-embed.ts +43 -1
  37. package/packages/daemon/src/lib/native-surface-context.ts +123 -0
  38. package/packages/daemon/src/lib/tmux-server-owner.ts +9 -0
  39. package/packages/daemon/src/server/native-surface-provider-upgrade.ts +103 -0
  40. package/packages/daemon/src/terminal/graphics/compress-chunk.ts +49 -0
  41. package/packages/daemon/src/terminal/graphics/native-surface-demand.ts +88 -0
  42. package/packages/daemon/src/terminal/graphics/native-surface-provider-channel.ts +161 -0
  43. package/packages/daemon/src/terminal/graphics/native-surface-provider.ts +308 -0
  44. package/packages/daemon/src/terminal/graphics/native-surface-viewer.ts +154 -0
  45. package/packages/daemon/src/terminal/mirror/mirror-service.ts +5 -0
  46. package/packages/daemon/src/terminal/mirror/session-channel.ts +31 -10
  47. package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +229 -2
  48. package/packages/daemon/src/terminal/pane-stream/runtime.ts +11 -1
@@ -1,4 +1,18 @@
1
+ import type { SurfaceViewerDemand } from "../graphics/native-surface-demand.ts";
1
2
  import {
3
+ TERMINAL_SURFACE_MAX_CONTROL_BYTES,
4
+ TERMINAL_SURFACE_CAPABILITY_VERSION,
5
+ type TerminalSurfaceReceiver,
6
+ } from "@tmux-ide/contracts";
7
+ import type {
8
+ NativeSurfaceProviderRegistry,
9
+ NativeSurfaceSession,
10
+ } from "../graphics/native-surface-provider.ts";
11
+ import { NativeSurfaceViewer } from "../graphics/native-surface-viewer.ts";
12
+ import { compressGraphicsChunk } from "../graphics/compress-chunk.ts";
13
+ import {
14
+ TERMINAL_GRAPHICS_COMPRESSED_CAPABILITY_VERSION,
15
+ TERMINAL_GRAPHICS_COMPRESSED_LOCAL_CAPABILITY_VERSION,
2
16
  TERMINAL_PROJECTION_CAPABILITY_VERSION,
3
17
  TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION,
4
18
  type TerminalProjectionReceipt,
@@ -259,6 +273,7 @@ export interface PaneStreamAdmissionSnapshot {
259
273
  }
260
274
 
261
275
  export interface PaneStreamAdmissionCoordinatorOptions {
276
+ readonly nativeSurfaces?: NativeSurfaceProviderRegistry;
262
277
  readonly localImages?: LocalImageOwner;
263
278
  readonly daemonInstanceId: string;
264
279
  readonly webSocketUrl: string;
@@ -405,6 +420,7 @@ function errorFrameCode(error: unknown): PaneStreamErrorFrameCode {
405
420
  * ticket digests; a daemon restart makes every prior descriptor inert.
406
421
  */
407
422
  export class PaneStreamAdmissionCoordinator {
423
+ readonly #nativeSurfaces: NativeSurfaceProviderRegistry | undefined;
408
424
  readonly #localImages: LocalImageOwner | undefined;
409
425
  readonly #instanceId: string;
410
426
  readonly #webSocketUrl: string;
@@ -439,6 +455,9 @@ export class PaneStreamAdmissionCoordinator {
439
455
  #shutdownPromise: Promise<void> | null = null;
440
456
 
441
457
  constructor(options: PaneStreamAdmissionCoordinatorOptions) {
458
+ this.#nativeSurfaces = options.nativeSurfaces;
459
+ if (this.#nativeSurfaces && this.#nativeSurfaces.generation !== options.daemonInstanceId)
460
+ throw new Error("Native surface provider generation mismatch");
442
461
  this.#localImages = options.localImages;
443
462
  this.#instanceId = BindingIdSchemaZ.parse(options.daemonInstanceId);
444
463
  this.#webSocketUrl = PaneStreamLoopbackWebSocketUrlSchemaZ.parse(options.webSocketUrl);
@@ -811,6 +830,7 @@ export class PaneStreamAdmissionCoordinator {
811
830
  let live: PaneStreamLiveConnection;
812
831
  try {
813
832
  live = new PaneStreamLiveConnection({
833
+ nativeSurfaces: this.#nativeSurfaces,
814
834
  localImages: this.#localImages,
815
835
  clientId: `psc-${this.#clientCounter}`,
816
836
  socket,
@@ -1030,6 +1050,7 @@ class PreAuthAdmission implements PaneStreamPreAuthAdmission {
1030
1050
  }
1031
1051
 
1032
1052
  interface LiveConnectionOptions {
1053
+ readonly nativeSurfaces?: NativeSurfaceProviderRegistry;
1033
1054
  readonly localImages?: LocalImageOwner;
1034
1055
  readonly clientId: string;
1035
1056
  readonly socket: DirectTerminalSocket;
@@ -1088,6 +1109,7 @@ interface PaneChannel {
1088
1109
  graphicsState: TerminalGraphicsState | null;
1089
1110
  graphicsZlib?: boolean;
1090
1111
  graphicsBinary?: boolean;
1112
+ graphicsCompressed?: boolean;
1091
1113
  binaryCells?: boolean;
1092
1114
  graphicsLocal?: boolean;
1093
1115
  graphicsInline?: Set<string>;
@@ -1122,6 +1144,13 @@ export function rawGraphicsState(state: TerminalGraphicsState): TerminalGraphics
1122
1144
  const GRAPHICS_SEND_WINDOW_BYTES = 2 * 1024 * 1024;
1123
1145
 
1124
1146
  export class PaneStreamLiveConnection {
1147
+ // One bounded native negotiation per redeemed socket; never reuse on reconnect.
1148
+ readonly #nativeSurfaces: NativeSurfaceProviderRegistry | undefined;
1149
+ #surfaceViewer: NativeSurfaceViewer | null = null;
1150
+ #surfaceDemands = new Map<string, SurfaceViewerDemand>();
1151
+ #surfaceNative = false;
1152
+ #surfaceSession: NativeSurfaceSession | null = null;
1153
+ #surfaceReceiver: TerminalSurfaceReceiver | null = null;
1125
1154
  readonly #localImages: LocalImageOwner | undefined;
1126
1155
  readonly #localLeases = new Map<string, { pane: string; expiresAt: number }>();
1127
1156
  readonly #clientId: string;
@@ -1248,6 +1277,7 @@ export class PaneStreamLiveConnection {
1248
1277
  }
1249
1278
 
1250
1279
  constructor(options: LiveConnectionOptions) {
1280
+ this.#nativeSurfaces = options.nativeSurfaces;
1251
1281
  this.#localImages = options.localImages;
1252
1282
  this.#clientId = options.clientId;
1253
1283
  this.#socket = options.socket;
@@ -1349,6 +1379,7 @@ export class PaneStreamLiveConnection {
1349
1379
  this.#sessionRuntimeBinding?.onAuthoritySnapshot?.((snapshot) =>
1350
1380
  this.#usesExplicitAuthority ? this.#sendAuthoritySnapshot(snapshot) : undefined,
1351
1381
  ) ?? null;
1382
+ if (!this.#descriptor.terminalDelivery) this.#observeSurfaceDemand(this.#descriptor.panes);
1352
1383
  void this.#subscribeAll();
1353
1384
  }
1354
1385
 
@@ -1356,6 +1387,12 @@ export class PaneStreamLiveConnection {
1356
1387
  if (this.#closed) return;
1357
1388
  this.#recordDiagnosticLifecycle("pane-stream-terminal", code, reason);
1358
1389
  this.#closed = true;
1390
+ for (const demand of this.#surfaceDemands.values()) demand.close();
1391
+ this.#surfaceDemands.clear();
1392
+ this.#surfaceViewer?.close();
1393
+ this.#surfaceViewer = null;
1394
+ this.#surfaceSession?.close();
1395
+ this.#surfaceSession = null;
1359
1396
  this.#cancelDrainTick?.();
1360
1397
  this.#cancelDrainTick = null;
1361
1398
  // Departure force-returns every ticket within this same tick.
@@ -1387,6 +1424,8 @@ export class PaneStreamLiveConnection {
1387
1424
  const delivery = channel.delivery;
1388
1425
  channel.delivery = null;
1389
1426
  channel.closed = true;
1427
+ this.#surfaceDemands.get(channel.semanticPaneId)?.close();
1428
+ this.#surfaceDemands.delete(channel.semanticPaneId);
1390
1429
  if (sub) closures.push(sub.close().catch(() => undefined));
1391
1430
  if (delivery) closures.push(delivery.close().catch(() => undefined));
1392
1431
  }
@@ -1854,10 +1893,27 @@ export class PaneStreamLiveConnection {
1854
1893
  await this.#awaitSemanticCredit(pane);
1855
1894
  }
1856
1895
 
1896
+ #observeSurfaceDemand(panes: readonly string[]): void {
1897
+ for (const pane of panes) {
1898
+ if (this.#surfaceDemands.has(pane)) continue;
1899
+ const demand = this.#nativeSurfaces?.demand.viewer(this.#descriptor.sessionName, [pane]);
1900
+ if (!demand) return;
1901
+ demand.native(this.#surfaceNative);
1902
+ this.#surfaceDemands.set(pane, demand);
1903
+ }
1904
+ }
1905
+
1906
+ #surfaceControl(type: string): void {
1907
+ if (type === "terminal-surface-offer") return;
1908
+ this.#surfaceNative = type === "terminal-surface-peer";
1909
+ for (const demand of this.#surfaceDemands.values()) demand.native(this.#surfaceNative);
1910
+ }
1911
+
1857
1912
  /** Announce the pane's kitty graphics state; the client pulls bytes it lacks. */
1858
1913
  #startGraphics(channel: PaneChannel): void {
1859
1914
  const graphics = channel.delivery?.graphics;
1860
1915
  if (!graphics) return;
1916
+ this.#observeSurfaceDemand([channel.semanticPaneId]);
1861
1917
  // Renderers that cannot inflate see raw formats; their bytes are inflated
1862
1918
  // when served.
1863
1919
  const zlib =
@@ -1871,7 +1927,14 @@ export class PaneStreamLiveConnection {
1871
1927
  (version) =>
1872
1928
  version === TERMINAL_GRAPHICS_LOCAL_CAPABILITY_VERSION ||
1873
1929
  version === TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION ||
1874
- version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION,
1930
+ version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION ||
1931
+ version === TERMINAL_GRAPHICS_COMPRESSED_LOCAL_CAPABILITY_VERSION,
1932
+ ) === true;
1933
+ channel.graphicsCompressed =
1934
+ this.#descriptor.terminalDelivery?.protocolVersions.some(
1935
+ (version) =>
1936
+ version === TERMINAL_GRAPHICS_COMPRESSED_CAPABILITY_VERSION ||
1937
+ version === TERMINAL_GRAPHICS_COMPRESSED_LOCAL_CAPABILITY_VERSION,
1875
1938
  ) === true;
1876
1939
  channel.graphicsInline = new Set();
1877
1940
  channel.graphicsBinary =
@@ -1882,7 +1945,9 @@ export class PaneStreamLiveConnection {
1882
1945
  version === TERMINAL_DELIVERY_BINARY_CAPABILITY_VERSION ||
1883
1946
  version === TERMINAL_PROJECTION_CAPABILITY_VERSION ||
1884
1947
  version === TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION ||
1885
- version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION,
1948
+ version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION ||
1949
+ version === TERMINAL_GRAPHICS_COMPRESSED_CAPABILITY_VERSION ||
1950
+ version === TERMINAL_GRAPHICS_COMPRESSED_LOCAL_CAPABILITY_VERSION,
1886
1951
  ) === true;
1887
1952
  // Renderers that report cell pixels learn up front that this daemon
1888
1953
  // accepts them, before any image exists.
@@ -1916,6 +1981,7 @@ export class PaneStreamLiveConnection {
1916
1981
  ...(cellPixels ? { cellPixels: true as const } : {}),
1917
1982
  ...(preciseCellPixels ? { preciseCellPixels: true as const } : {}),
1918
1983
  ...(channel.graphicsBinary ? { binaryBlobs: true as const } : {}),
1984
+ ...(channel.graphicsCompressed ? { compressedBlobs: true as const } : {}),
1919
1985
  ...(channel.graphicsLocal ? { localFiles: true as const } : {}),
1920
1986
  },
1921
1987
  });
@@ -1933,6 +1999,7 @@ export class PaneStreamLiveConnection {
1933
1999
  cellPixels: true,
1934
2000
  ...(preciseCellPixels ? { preciseCellPixels: true as const } : {}),
1935
2001
  ...(channel.graphicsBinary ? { binaryBlobs: true as const } : {}),
2002
+ ...(channel.graphicsCompressed ? { compressedBlobs: true as const } : {}),
1936
2003
  ...(channel.graphicsLocal ? { localFiles: true as const } : {}),
1937
2004
  },
1938
2005
  });
@@ -2046,6 +2113,27 @@ export class PaneStreamLiveConnection {
2046
2113
  if (offset > 0 && graphics.image(digest) !== image) break;
2047
2114
  const end = Math.min(total, offset + TERMINAL_GRAPHICS_BLOB_CHUNK_BYTES);
2048
2115
  if (channel.graphicsBinary) {
2116
+ const header = {
2117
+ pane: channel.semanticPaneId,
2118
+ blob: {
2119
+ digest,
2120
+ format,
2121
+ width: image.width,
2122
+ height: image.height,
2123
+ total,
2124
+ offset,
2125
+ },
2126
+ };
2127
+ const compressed = channel.graphicsCompressed
2128
+ ? await compressGraphicsChunk(header, data.subarray(offset, end))
2129
+ : null;
2130
+ // Compression yields: a pane may close or replace this image meanwhile.
2131
+ if (this.#closed || channel.closed) return;
2132
+ if (graphics.image(digest) !== image) break;
2133
+ if (compressed) {
2134
+ this.#sendEncoded(null, Buffer.from(compressed), true);
2135
+ continue;
2136
+ }
2049
2137
  this.#sendEncoded(
2050
2138
  null,
2051
2139
  Buffer.from(
@@ -2223,6 +2311,8 @@ export class PaneStreamLiveConnection {
2223
2311
  #emitClosed(channel: PaneChannel): void {
2224
2312
  if (channel.closed) return;
2225
2313
  channel.closed = true;
2314
+ this.#surfaceDemands.get(channel.semanticPaneId)?.close();
2315
+ this.#surfaceDemands.delete(channel.semanticPaneId);
2226
2316
  const sub = channel.sub;
2227
2317
  channel.sub = null;
2228
2318
  if (sub) void sub.close().catch(() => undefined);
@@ -2678,6 +2768,143 @@ export class PaneStreamLiveConnection {
2678
2768
  this.#acceptSemanticIntent(frame.operationId, frame.intent);
2679
2769
  return;
2680
2770
  }
2771
+ if (frame.type === "terminal-surface-watch") {
2772
+ if (
2773
+ byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES ||
2774
+ frame.generation !== this.#sessionRuntimeBinding?.generation ||
2775
+ !this.#descriptor.terminalDelivery?.protocolVersions.includes(
2776
+ TERMINAL_SURFACE_CAPABILITY_VERSION,
2777
+ ) ||
2778
+ !this.#nativeSurfaces ||
2779
+ this.#surfaceReceiver
2780
+ )
2781
+ return this.#failProtocol("protocol-error");
2782
+ if (this.#surfaceViewer) return;
2783
+ this.#surfaceViewer = new NativeSurfaceViewer(
2784
+ this.#nativeSurfaces,
2785
+ this.#descriptor.panes,
2786
+ (control) => {
2787
+ if (this.#closed) return false;
2788
+ this.#surfaceControl(control.type);
2789
+ this.#sendFrame(null, control);
2790
+ return !this.#closed;
2791
+ },
2792
+ (pane, generation) => {
2793
+ const channel = this.#panes.get(pane);
2794
+ const address = channel?.deliveryAddress;
2795
+ return !this.#closed &&
2796
+ channel &&
2797
+ !channel.closed &&
2798
+ channel.delivery &&
2799
+ address?.generation === generation
2800
+ ? address.deliveryNonce
2801
+ : null;
2802
+ },
2803
+ );
2804
+ this.#surfaceViewer.start();
2805
+ return;
2806
+ }
2807
+ if (frame.type === "terminal-surface-receiver") {
2808
+ if (this.#surfaceViewer) {
2809
+ if (byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES || !this.#surfaceViewer.receive(frame))
2810
+ this.#failProtocol("protocol-error");
2811
+ return;
2812
+ }
2813
+ if (
2814
+ byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES ||
2815
+ !this.#descriptor.terminalDelivery ||
2816
+ frame.generation !== this.#sessionRuntimeBinding?.generation
2817
+ ) {
2818
+ return this.#failProtocol("protocol-error");
2819
+ }
2820
+ if (this.#surfaceReceiver) {
2821
+ // Parsed schema order is deterministic. A duplicate cannot change the
2822
+ // challenge, kernel identity or endpoint, nor amplify fallback replies.
2823
+ if (JSON.stringify(frame) !== JSON.stringify(this.#surfaceReceiver)) {
2824
+ return this.#failProtocol("protocol-error");
2825
+ }
2826
+ return;
2827
+ }
2828
+ this.#surfaceReceiver = frame;
2829
+ if (
2830
+ this.#descriptor.terminalDelivery.protocolVersions.includes(
2831
+ TERMINAL_SURFACE_CAPABILITY_VERSION,
2832
+ )
2833
+ ) {
2834
+ const native =
2835
+ this.#nativeSurfaces?.open(
2836
+ frame,
2837
+ this.#descriptor.panes,
2838
+ (control) => {
2839
+ if (this.#closed) return false;
2840
+ this.#surfaceControl(control.type);
2841
+ this.#sendFrame(null, control);
2842
+ return !this.#closed;
2843
+ },
2844
+ (pane, generation) => {
2845
+ const channel = this.#panes.get(pane);
2846
+ const address = channel?.deliveryAddress;
2847
+ if (
2848
+ this.#closed ||
2849
+ !channel ||
2850
+ channel.closed ||
2851
+ !channel.delivery ||
2852
+ !address ||
2853
+ address.generation !== generation
2854
+ )
2855
+ return null;
2856
+ return address.deliveryNonce;
2857
+ },
2858
+ ) ?? null;
2859
+ if (this.#closed) {
2860
+ native?.close();
2861
+ return;
2862
+ }
2863
+ this.#surfaceSession = native;
2864
+ if (native) return;
2865
+ }
2866
+ // No provider/capability means no PID/slot lookup and no inferred locality.
2867
+ this.#sendFrame(null, {
2868
+ type: "terminal-surface-fallback",
2869
+ version: 1,
2870
+ generation: frame.generation,
2871
+ connectionNonce: frame.connectionNonce,
2872
+ reason: "unsupported",
2873
+ });
2874
+ return;
2875
+ }
2876
+ if (frame.type === "terminal-surface-fallback") {
2877
+ if (this.#surfaceViewer) {
2878
+ if (byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES || !this.#surfaceViewer.fallback(frame))
2879
+ this.#failProtocol("protocol-error");
2880
+ return;
2881
+ }
2882
+ if (
2883
+ byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES ||
2884
+ frame.generation !== this.#surfaceReceiver?.generation ||
2885
+ frame.connectionNonce !== this.#surfaceReceiver.connectionNonce
2886
+ ) {
2887
+ return this.#failProtocol("protocol-error");
2888
+ }
2889
+ this.#surfaceSession?.fallback(frame.reason);
2890
+ return;
2891
+ }
2892
+ if (frame.type === "terminal-surface-retired") {
2893
+ if (this.#surfaceViewer) {
2894
+ if (byteLength > TERMINAL_SURFACE_MAX_CONTROL_BYTES || !this.#surfaceViewer.retire(frame))
2895
+ this.#failProtocol("protocol-error");
2896
+ return;
2897
+ }
2898
+ if (
2899
+ byteLength <= TERMINAL_SURFACE_MAX_CONTROL_BYTES &&
2900
+ frame.generation === this.#surfaceReceiver?.generation &&
2901
+ frame.connectionNonce === this.#surfaceReceiver.connectionNonce &&
2902
+ this.#surfaceSession?.retire(frame.sequence)
2903
+ )
2904
+ return;
2905
+ // Unissued/replayed ACKs must not retire another socket's native storage.
2906
+ return this.#failProtocol("protocol-error");
2907
+ }
2681
2908
  if (frame.type === "terminal-graphics-request") {
2682
2909
  this.#requestGraphics(frame.pane, frame.digests, frame.preferInline === true);
2683
2910
  return;
@@ -1,4 +1,5 @@
1
1
  import type { LocalImageOwner } from "../graphics/local-image-owner.ts";
2
+ import { NativeSurfaceProviderRegistry } from "../graphics/native-surface-provider.ts";
2
3
  import type { SemanticPaneCatalog } from "../attachments/semantic-pane-catalog.ts";
3
4
  import type { SessionRuntimeRegistry } from "../session-runtime/registry.ts";
4
5
  import { SessionRuntimeTransportBinder } from "../session-runtime/transport-binding.ts";
@@ -10,6 +11,7 @@ import {
10
11
  } from "./pane-stream-websocket.ts";
11
12
 
12
13
  export interface PaneStreamRuntimeOptions {
14
+ readonly nativeSurfaces?: NativeSurfaceProviderRegistry;
13
15
  readonly localImages?: LocalImageOwner;
14
16
  readonly daemonInstanceId: string;
15
17
  readonly webSocketUrl: string;
@@ -20,7 +22,7 @@ export interface PaneStreamRuntimeOptions {
20
22
  readonly observability?: SessionRuntimeObservability;
21
23
  readonly admission?: Omit<
22
24
  PaneStreamAdmissionCoordinatorOptions,
23
- "daemonInstanceId" | "webSocketUrl" | "leaseManager" | "mirror"
25
+ "daemonInstanceId" | "webSocketUrl" | "leaseManager" | "mirror" | "nativeSurfaces"
24
26
  >;
25
27
  }
26
28
 
@@ -31,9 +33,15 @@ export interface PaneStreamRuntimeOptions {
31
33
  */
32
34
  export class PaneStreamRuntime {
33
35
  readonly coordinator: PaneStreamAdmissionCoordinator;
36
+ readonly nativeSurfaces: NativeSurfaceProviderRegistry | undefined;
34
37
  #disposePromise: Promise<void> | null = null;
35
38
 
36
39
  constructor(options: PaneStreamRuntimeOptions) {
40
+ this.nativeSurfaces =
41
+ options.nativeSurfaces ??
42
+ (process.platform === "darwin"
43
+ ? new NativeSurfaceProviderRegistry(options.daemonInstanceId)
44
+ : undefined);
37
45
  const leaseManager = new PaneStreamLeaseManager({
38
46
  daemonInstanceId: options.daemonInstanceId,
39
47
  });
@@ -41,6 +49,7 @@ export class PaneStreamRuntime {
41
49
  this.coordinator = new PaneStreamAdmissionCoordinator({
42
50
  ...options.admission,
43
51
  localImages: options.localImages,
52
+ nativeSurfaces: this.nativeSurfaces,
44
53
  daemonInstanceId: options.daemonInstanceId,
45
54
  webSocketUrl: options.webSocketUrl,
46
55
  leaseManager,
@@ -69,6 +78,7 @@ export class PaneStreamRuntime {
69
78
  dispose(): Promise<void> {
70
79
  if (!this.#disposePromise) {
71
80
  this.#disposePromise = (async () => {
81
+ this.nativeSurfaces?.close();
72
82
  await this.coordinator.shutdown();
73
83
  })();
74
84
  }