tmux-ide 2.9.2 → 2.9.3
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/README.md +1 -1
- package/bin/cli.js +2071 -625
- package/package.json +14 -5
- package/packages/contracts/src/desktop-host.ts +3 -0
- package/packages/contracts/src/saved-machines.ts +19 -0
- package/packages/contracts/src/terminal-replica.ts +1 -1
- package/packages/core/package.json +3 -2
- package/packages/core/src/terminal-delivery.ts +51 -3
- package/packages/core/src/terminal-fnv64-wasm.ts +4 -3
- package/packages/core/src/terminal-replica-hash-cache.ts +129 -31
- package/packages/core/src/terminal-replica-owned-row.ts +27 -0
- package/packages/core/src/terminal-replica.ts +13 -28
- package/packages/daemon/dist/command-center/daemon-fleet-facts-observer.js +96 -10
- package/packages/daemon/dist/command-center/resources/saved-machine-route.js +40 -1
- package/packages/daemon/dist/command-center/ws-events.js +5 -24
- package/packages/daemon/dist/lib/canonical-daemon-bootstrap.js +8 -0
- package/packages/daemon/dist/lib/canonical-daemon.js +28 -2
- package/packages/daemon/dist/lib/daemon-embed.js +1 -1
- package/packages/daemon/dist/lib/daemon-service-manager.js +122 -0
- package/packages/daemon/dist/lib/daemon-service-plan.js +127 -0
- package/packages/daemon/dist/lib/daemon-service.js +259 -0
- package/packages/daemon/dist/lib/install-origin.js +2 -0
- package/packages/daemon/dist/lib/local-fleet-request.js +9 -3
- package/packages/daemon/dist/lib/log.js +6 -10
- package/packages/daemon/dist/lib/managed-installer-update.js +67 -0
- package/packages/daemon/dist/lib/{tui-download-lock.js → private-operation-lock.js} +11 -11
- package/packages/daemon/dist/lib/remote-daemon-info.js +3 -2
- package/packages/daemon/dist/lib/remote-daemon-lifecycle.js +4 -4
- package/packages/daemon/dist/lib/remote-tmux-command.js +10 -0
- package/packages/daemon/dist/lib/runtime-namespace.js +28 -16
- package/packages/daemon/dist/lib/saved-machines.js +40 -2
- package/packages/daemon/dist/lib/ssh-daemon-transport.js +24 -9
- package/packages/daemon/dist/lib/tmux-server-owner.js +1 -1
- package/packages/daemon/dist/lib/tmux-server-registration.js +21 -3
- package/packages/daemon/dist/lib/tui-binary.js +2 -2
- package/packages/daemon/dist/lib/update.js +18 -0
- package/packages/daemon/dist/lib/workspace-multiplexer-verbs.js +24 -1
- package/packages/daemon/dist/machines.js +40 -4
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +2 -2
- package/packages/daemon/dist/terminal/attachments/direct-websocket.js +4 -1
- package/packages/daemon/dist/terminal/attachments/native-runtime.js +22 -13
- package/packages/daemon/dist/terminal/mirror/mirror-service.js +101 -3
- package/packages/daemon/dist/terminal/mirror/registered-window-guard.js +62 -0
- package/packages/daemon/dist/terminal/mirror/session-channel.js +75 -4
- package/packages/daemon/dist/terminal/mirror/shared-window-index.js +45 -0
- package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +11 -1
- package/packages/daemon/dist/terminal/session-runtime/multiplexer-backend.js +1 -0
- package/packages/daemon/dist/terminal/session-runtime/registry.js +56 -7
- package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-executor.js +3 -1
- package/packages/daemon/dist/tui/mirror/dialog-model.js +2 -35
- package/packages/daemon/dist/tui/mirror/runtime/application-daemon-authority-owner.js +8 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-home-fleet.js +20 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-agents.js +7 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-authority.js +59 -9
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +27 -19
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +23 -18
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-startup.js +31 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-palette-search-owner.js +7 -3
- package/packages/daemon/dist/tui/mirror/runtime/application-performance-log.js +3 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-input-ingress.js +146 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +201 -50
- package/packages/daemon/dist/tui/mirror/runtime/bounded-performance-record-writer.js +50 -10
- package/packages/daemon/dist/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.js +6 -5
- package/packages/daemon/dist/tui/mirror/runtime/terminal-pane-input-router.js +3 -0
- package/packages/daemon/dist/tui/mirror/runtime/workspace-terminal-fast-lane.js +8 -2
- package/packages/daemon/dist/tui/mirror/terminal-text.js +35 -0
- package/packages/daemon/dist/tui/mirror/workspace/connection-feedback.js +5 -3
- package/packages/daemon/src/command-center/daemon-fleet-facts-observer.ts +100 -10
- package/packages/daemon/src/command-center/resources/saved-machine-route.ts +48 -1
- package/packages/daemon/src/command-center/ws-events.ts +5 -25
- package/packages/daemon/src/lib/canonical-daemon-bootstrap.ts +12 -0
- package/packages/daemon/src/lib/canonical-daemon.ts +44 -2
- package/packages/daemon/src/lib/daemon-embed.ts +1 -0
- package/packages/daemon/src/lib/daemon-service-manager.ts +152 -0
- package/packages/daemon/src/lib/daemon-service-plan.ts +153 -0
- package/packages/daemon/src/lib/daemon-service.ts +301 -0
- package/packages/daemon/src/lib/install-origin.ts +5 -0
- package/packages/daemon/src/lib/local-fleet-request.ts +19 -3
- package/packages/daemon/src/lib/log.ts +13 -9
- package/packages/daemon/src/lib/managed-installer-update.ts +79 -0
- package/packages/daemon/src/lib/{tui-download-lock.ts → private-operation-lock.ts} +15 -11
- package/packages/daemon/src/lib/remote-daemon-info.ts +3 -3
- package/packages/daemon/src/lib/remote-daemon-lifecycle.ts +4 -4
- package/packages/daemon/src/lib/remote-tmux-command.ts +12 -0
- package/packages/daemon/src/lib/runtime-namespace.ts +27 -18
- package/packages/daemon/src/lib/saved-machines.ts +40 -1
- package/packages/daemon/src/lib/ssh-daemon-transport.ts +25 -7
- package/packages/daemon/src/lib/tmux-server-owner.ts +1 -0
- package/packages/daemon/src/lib/tmux-server-registration.ts +21 -3
- package/packages/daemon/src/lib/tui-binary.ts +2 -2
- package/packages/daemon/src/lib/update.ts +24 -0
- package/packages/daemon/src/lib/workspace-multiplexer-verbs.ts +22 -0
- package/packages/daemon/src/machines.ts +53 -5
- package/packages/daemon/src/terminal/attachments/direct-websocket.ts +8 -2
- package/packages/daemon/src/terminal/attachments/native-runtime.ts +28 -13
- package/packages/daemon/src/terminal/mirror/mirror-service.ts +105 -3
- package/packages/daemon/src/terminal/mirror/registered-window-guard.ts +66 -0
- package/packages/daemon/src/terminal/mirror/session-channel.ts +92 -4
- package/packages/daemon/src/terminal/mirror/shared-window-index.ts +42 -0
- package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +12 -1
- package/packages/daemon/src/terminal/session-runtime/multiplexer-backend.ts +2 -0
- package/packages/daemon/src/terminal/session-runtime/registry.ts +70 -8
- package/packages/daemon/src/terminal/session-runtime/semantic-mutation-executor.ts +3 -1
- package/packages/daemon/src/tui/mirror/dialog-model.ts +2 -32
- package/packages/daemon/src/tui/mirror/runtime/application-daemon-authority-owner.ts +8 -0
- package/packages/daemon/src/tui/mirror/runtime/application-home-fleet.ts +19 -2
- package/packages/daemon/src/tui/mirror/runtime/application-machine-agents.ts +6 -1
- package/packages/daemon/src/tui/mirror/runtime/application-machine-authority.ts +60 -11
- package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +39 -33
- package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +31 -23
- package/packages/daemon/src/tui/mirror/runtime/application-machine-startup.ts +38 -2
- package/packages/daemon/src/tui/mirror/runtime/application-palette-search-owner.ts +7 -4
- package/packages/daemon/src/tui/mirror/runtime/application-performance-log.ts +3 -0
- package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +1 -1
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +47 -46
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-input-ingress.ts +165 -2
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +207 -53
- package/packages/daemon/src/tui/mirror/runtime/bounded-performance-record-writer.ts +49 -9
- package/packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.ts +6 -5
- package/packages/daemon/src/tui/mirror/runtime/terminal-pane-input-router.ts +4 -0
- package/packages/daemon/src/tui/mirror/runtime/workspace-terminal-fast-lane.ts +8 -3
- package/packages/daemon/src/tui/mirror/terminal-text.ts +32 -0
- package/packages/daemon/src/tui/mirror/workspace/connection-feedback.ts +6 -3
- package/packages/daemon-client/src/fleet-connection-status.ts +10 -1
- package/packages/daemon-client/src/workspace-client.ts +4 -1
- package/packages/contracts/src/__tests__/actions-contract.test.ts +0 -119
- package/packages/contracts/src/__tests__/agent-graph-overlay.test.ts +0 -428
- package/packages/contracts/src/__tests__/app-window-mutation.test.ts +0 -51
- package/packages/contracts/src/__tests__/app-window-state.test.ts +0 -34
- package/packages/contracts/src/__tests__/application-shell.test.ts +0 -645
- package/packages/contracts/src/__tests__/automation-operations.test.ts +0 -78
- package/packages/contracts/src/__tests__/causal-cell.test.ts +0 -47
- package/packages/contracts/src/__tests__/client-view-state.test.ts +0 -115
- package/packages/contracts/src/__tests__/cohesion-fixture.test.ts +0 -106
- package/packages/contracts/src/__tests__/commands.test.ts +0 -86
- package/packages/contracts/src/__tests__/control.test.ts +0 -154
- package/packages/contracts/src/__tests__/daemon-events.test.ts +0 -405
- package/packages/contracts/src/__tests__/daemon-resources.test.ts +0 -143
- package/packages/contracts/src/__tests__/daemon-wire.test.ts +0 -149
- package/packages/contracts/src/__tests__/desktop-host.test.ts +0 -269
- package/packages/contracts/src/__tests__/desktop-missions.test.ts +0 -215
- package/packages/contracts/src/__tests__/experience-conformance.test.ts +0 -153
- package/packages/contracts/src/__tests__/experience-kernel-boundary.test.ts +0 -113
- package/packages/contracts/src/__tests__/experience-shell.test.ts +0 -65
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v2.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v3.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v4.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/scoped-receipt.ts +0 -62
- package/packages/contracts/src/__tests__/fleet-agent-graph.test.ts +0 -231
- package/packages/contracts/src/__tests__/fleet-catalog.test.ts +0 -270
- package/packages/contracts/src/__tests__/fleet-lifecycle.test.ts +0 -62
- package/packages/contracts/src/__tests__/focus-overlay.test.ts +0 -110
- package/packages/contracts/src/__tests__/ide-config.test.ts +0 -64
- package/packages/contracts/src/__tests__/interaction-evidence.test.ts +0 -288
- package/packages/contracts/src/__tests__/interaction-receipt-v2.test.ts +0 -72
- package/packages/contracts/src/__tests__/issue-error.test.ts +0 -78
- package/packages/contracts/src/__tests__/mission-projections.test.ts +0 -186
- package/packages/contracts/src/__tests__/missions.test.ts +0 -272
- package/packages/contracts/src/__tests__/multiplexer-verbs.test.ts +0 -242
- package/packages/contracts/src/__tests__/pane-appearance.test.ts +0 -146
- package/packages/contracts/src/__tests__/pane-read-result.test.ts +0 -55
- package/packages/contracts/src/__tests__/pane-stream.test.ts +0 -536
- package/packages/contracts/src/__tests__/pane-widget-marker.test.ts +0 -327
- package/packages/contracts/src/__tests__/performance-metrics.test.ts +0 -73
- package/packages/contracts/src/__tests__/performance-qualification.test.ts +0 -137
- package/packages/contracts/src/__tests__/rich-card-widget.test.ts +0 -29
- package/packages/contracts/src/__tests__/session-runtime.test.ts +0 -512
- package/packages/contracts/src/__tests__/startup-readiness.test.ts +0 -409
- package/packages/contracts/src/__tests__/terminal-attachment-stream.test.ts +0 -72
- package/packages/contracts/src/__tests__/terminal-attachments.test.ts +0 -329
- package/packages/contracts/src/__tests__/terminal-delivery.test.ts +0 -88
- package/packages/contracts/src/__tests__/terminal-runtime-inventory.test.ts +0 -68
- package/packages/contracts/src/__tests__/terminals.test.ts +0 -87
- package/packages/contracts/src/__tests__/tmux-server-scope.test.ts +0 -166
- package/packages/contracts/src/__tests__/transport-contract-loading.test.ts +0 -91
- package/packages/contracts/src/__tests__/visual-tokens.test.ts +0 -224
- package/packages/contracts/src/__tests__/window-links.test.ts +0 -243
- package/packages/contracts/src/__tests__/workspace-changes-resource.test.ts +0 -317
- package/packages/contracts/src/__tests__/workspace-changes-view.test.ts +0 -122
- package/packages/contracts/src/__tests__/workspace-config.test.ts +0 -294
- package/packages/contracts/src/__tests__/workspace-files-resource.test.ts +0 -261
- package/packages/contracts/src/__tests__/workspace-files-tree.test.ts +0 -147
- package/packages/contracts/src/__tests__/workspace-pane-creation.test.ts +0 -249
- package/packages/contracts/src/__tests__/workspace-promotion.test.ts +0 -94
- package/packages/contracts/src/__tests__/workspace-resource-identity.test.ts +0 -90
- package/packages/contracts/src/daemon-resource-request.test.ts +0 -192
- package/packages/contracts/src/native-operation-identity.test.ts +0 -23
- package/packages/contracts/src/workspace-admission.test.ts +0 -21
- package/packages/contracts/src/workspace-catalog-resource.test.ts +0 -146
- package/packages/core/src/agent-provisioning.test.ts +0 -42
- package/packages/core/src/application-shell-session.test.ts +0 -105
- package/packages/core/src/interaction-evidence.test.ts +0 -134
- package/packages/core/src/interaction-flow.test.ts +0 -66
- package/packages/core/src/interaction-receipts.test.ts +0 -671
- package/packages/core/src/navigator.test.ts +0 -34
- package/packages/core/src/optimistic-projection-conformance.test.ts +0 -75
- package/packages/core/src/optimistic-projection.test.ts +0 -118
- package/packages/core/src/performance-metrics.test.ts +0 -121
- package/packages/core/src/performance-qualification.test.ts +0 -283
- package/packages/core/src/saved-machines.test.ts +0 -80
- package/packages/core/src/terminal-compact-cold.test.ts +0 -87
- package/packages/core/src/terminal-conformance.test.ts +0 -48
- package/packages/core/src/terminal-delivery-cache-lifetime.test.ts +0 -167
- package/packages/core/src/terminal-delivery-encode-cooperative.test.ts +0 -140
- package/packages/core/src/terminal-delivery-representation-hash.test.ts +0 -77
- package/packages/core/src/terminal-delivery.test.ts +0 -1471
- package/packages/core/src/terminal-fnv64-wasm.test.ts +0 -61
- package/packages/core/src/terminal-replica-cold.test.ts +0 -31
- package/packages/core/src/terminal-replica-cooperative.test.ts +0 -170
- package/packages/core/src/terminal-replica-hash-cache.test.ts +0 -283
- package/packages/core/src/terminal-replica.test.ts +0 -347
- package/packages/core/src/workspace-selection.test.ts +0 -63
- package/packages/daemon/dist/__tests__/support.js +0 -33
- package/packages/daemon/dist/lib/__tests__/fixtures/workspace-state-writer.js +0 -58
- package/packages/daemon/dist/lib/__tests__/installed-recovery-fixture.js +0 -400
- package/packages/daemon/dist/terminal/__tests__/MockPtyAdapter.js +0 -182
- package/packages/daemon/dist/terminal/mirror/__tests__/scripted-channel.js +0 -98
- package/packages/daemon/dist/terminal/mirror/__tests__/simulated-channel.js +0 -156
- package/packages/daemon/src/tui/mirror/__snapshots__/activity-surface-renderer.test.tsx.snap +0 -42
- package/packages/daemon/src/tui/mirror/__snapshots__/changes-terminal-surface-renderer.test.tsx.snap +0 -267
- package/packages/daemon/src/tui/mirror/__snapshots__/home-files-surface-renderer.test.tsx.snap +0 -267
- package/packages/daemon/src/tui/mirror/__snapshots__/missions-surface-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/__snapshots__/recipes-gallery-renderer.test.tsx.snap +0 -141
- package/packages/daemon/src/tui/mirror/__snapshots__/shell-chrome-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/runtime/__snapshots__/application-shell-view-renderer.test.tsx.snap +0 -87
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/agent-terminal-canvas-renderer.test.tsx.snap +0 -348
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/application-shell-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/command-palette-surface-renderer.test.tsx.snap +0 -255
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/pane-frame-renderer.test.tsx.snap +0 -525
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/terminal-pane-header-polish-renderer.test.tsx.snap +0 -31
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/workbench-shell-renderer.test.tsx.snap +0 -134
- package/packages/daemon-client/src/application-shell-session.test.ts +0 -196
- package/packages/daemon-client/src/automation-client.test.ts +0 -214
- package/packages/daemon-client/src/bootstrap-coordinator.test.ts +0 -145
- package/packages/daemon-client/src/connection-supervisor.test.ts +0 -109
- package/packages/daemon-client/src/direct-application-shell-transport.test.ts +0 -941
- package/packages/daemon-client/src/first-latest-coordinator.test.ts +0 -175
- package/packages/daemon-client/src/fleet-dial-scheduler.test.ts +0 -114
- package/packages/daemon-client/src/fleet-environments.test.ts +0 -40
- package/packages/daemon-client/src/generation-bound-store-timers.test.ts +0 -74
- package/packages/daemon-client/src/owner-action-client.test.ts +0 -245
- package/packages/daemon-client/src/pane-stream-client.test.ts +0 -2841
- package/packages/daemon-client/src/pane-stream-clock-calibration.test.ts +0 -207
- package/packages/daemon-client/src/pane-stream-loopback-benchmark.test.ts +0 -51
- package/packages/daemon-client/src/push-resource-session.test.ts +0 -509
- package/packages/daemon-client/src/resource-replica.test.ts +0 -186
- package/packages/daemon-client/src/runtime-resource-ledger.test.ts +0 -18
- package/packages/daemon-client/src/scoped-tmux-server-transport.test.ts +0 -114
- package/packages/daemon-client/src/terminal-fast-lane.test.ts +0 -835
- package/packages/daemon-client/src/tmux-server-client.test.ts +0 -279
- package/packages/daemon-client/src/tmux-server-interaction-events.test.ts +0 -230
- package/packages/daemon-client/src/workspace-catalog-v2.test.ts +0 -109
- package/packages/daemon-client/src/workspace-client-boundaries.test.ts +0 -59
- package/packages/daemon-client/src/workspace-client-operations.test.ts +0 -96
- package/packages/daemon-client/src/workspace-client.test.ts +0 -2912
- package/packages/daemon-client/src/workspace-event-supervisor.test.ts +0 -1086
- package/packages/daemon-client/src/workspace-pane-client.test.ts +0 -80
- package/packages/presentation/src/presentation-contract.test.ts +0 -63
- package/packages/tmux-bridge/src/runner.test.ts +0 -614
- package/packages/tmux-bridge/src/targeting.test.ts +0 -107
|
@@ -29,6 +29,154 @@ export function createApplicationTerminalInputIngress(
|
|
|
29
29
|
) {
|
|
30
30
|
const pending = createApplicationPendingTerminalInputOwner();
|
|
31
31
|
let ownsNote = false;
|
|
32
|
+
type RecoveryScope = {
|
|
33
|
+
owner: OpenTuiSessionOwner;
|
|
34
|
+
connection: NonNullable<OpenTuiGenerationHostSnapshot["connection"]>;
|
|
35
|
+
client: NonNullable<OpenTuiGenerationHostSnapshot["client"]>;
|
|
36
|
+
daemon: string;
|
|
37
|
+
clientGeneration: number;
|
|
38
|
+
session: string;
|
|
39
|
+
pane: string;
|
|
40
|
+
};
|
|
41
|
+
let recovery: {
|
|
42
|
+
scope: RecoveryScope;
|
|
43
|
+
inputs: Array<() => Promise<boolean>>;
|
|
44
|
+
weight: number;
|
|
45
|
+
admitted: number;
|
|
46
|
+
draining: boolean;
|
|
47
|
+
timer: ReturnType<typeof setTimeout>;
|
|
48
|
+
} | null = null;
|
|
49
|
+
const scope = (): RecoveryScope | null => {
|
|
50
|
+
const active = generation();
|
|
51
|
+
const owner = sessionOwner();
|
|
52
|
+
const pane = focusedPane();
|
|
53
|
+
const session = owner?.sessionName();
|
|
54
|
+
if (
|
|
55
|
+
!active ||
|
|
56
|
+
!["live", "rebinding"].includes(active.status) ||
|
|
57
|
+
!active.connection ||
|
|
58
|
+
!active.client ||
|
|
59
|
+
!active.daemonGeneration ||
|
|
60
|
+
!owner ||
|
|
61
|
+
!session ||
|
|
62
|
+
!pane
|
|
63
|
+
)
|
|
64
|
+
return null;
|
|
65
|
+
try {
|
|
66
|
+
const clientGeneration = active.client.getSnapshot().generation;
|
|
67
|
+
if (!Number.isSafeInteger(clientGeneration)) return null;
|
|
68
|
+
return {
|
|
69
|
+
owner,
|
|
70
|
+
connection: active.connection,
|
|
71
|
+
client: active.client,
|
|
72
|
+
daemon: active.daemonGeneration,
|
|
73
|
+
clientGeneration: clientGeneration!,
|
|
74
|
+
session: session!,
|
|
75
|
+
pane,
|
|
76
|
+
};
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const sameScope = (left: RecoveryScope, right: RecoveryScope | null): boolean =>
|
|
82
|
+
right !== null &&
|
|
83
|
+
left.owner === right.owner &&
|
|
84
|
+
left.connection === right.connection &&
|
|
85
|
+
left.client === right.client &&
|
|
86
|
+
left.daemon === right.daemon &&
|
|
87
|
+
left.clientGeneration === right.clientGeneration &&
|
|
88
|
+
left.session === right.session &&
|
|
89
|
+
left.pane === right.pane;
|
|
90
|
+
let recoveryVersion = 0;
|
|
91
|
+
const cancelRecovery = () => {
|
|
92
|
+
recoveryVersion++;
|
|
93
|
+
if (recovery) clearTimeout(recovery.timer);
|
|
94
|
+
recovery = null;
|
|
95
|
+
};
|
|
96
|
+
const recover = (
|
|
97
|
+
inputs: ReturnType<typeof terminalInputsForPaste>,
|
|
98
|
+
bytes: Uint8Array,
|
|
99
|
+
kind: "input" | "paste",
|
|
100
|
+
): boolean => {
|
|
101
|
+
if (
|
|
102
|
+
pending.snapshot().sessionName !== null ||
|
|
103
|
+
(!recovery && generation()?.status !== "rebinding")
|
|
104
|
+
)
|
|
105
|
+
return false;
|
|
106
|
+
const target = scope();
|
|
107
|
+
if (!target) return false;
|
|
108
|
+
if (recovery && !sameScope(recovery.scope, target)) cancelRecovery();
|
|
109
|
+
if (!recovery) {
|
|
110
|
+
const timer = setTimeout(() => {
|
|
111
|
+
cancelRecovery();
|
|
112
|
+
setNote("terminal recovery timed out · queued input was not sent");
|
|
113
|
+
}, 5_000);
|
|
114
|
+
timer.unref?.();
|
|
115
|
+
recovery = { scope: target, inputs: [], weight: 0, admitted: 0, draining: false, timer };
|
|
116
|
+
}
|
|
117
|
+
const weight = bytes.byteLength;
|
|
118
|
+
if (recovery.admitted >= 64 || recovery.weight + weight > 1024 * 1024) {
|
|
119
|
+
setNote("terminal input queue full · wait for the session to connect");
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
recovery.weight += weight;
|
|
123
|
+
recovery.admitted++;
|
|
124
|
+
const version = recoveryVersion;
|
|
125
|
+
const parserOrigin = captureOrigin()
|
|
126
|
+
? {
|
|
127
|
+
origin: kind === "paste" ? ("bracketed-paste" as const) : ("keyboard" as const),
|
|
128
|
+
payload: Buffer.from(bytes),
|
|
129
|
+
}
|
|
130
|
+
: undefined;
|
|
131
|
+
recovery.inputs.push(async () => {
|
|
132
|
+
for (const [index, input] of inputs.entries()) {
|
|
133
|
+
if (
|
|
134
|
+
version !== recoveryVersion ||
|
|
135
|
+
!sameScope(target, scope()) ||
|
|
136
|
+
generation()?.status !== "live" ||
|
|
137
|
+
!(await interaction.sendInputToPane(
|
|
138
|
+
target.pane,
|
|
139
|
+
input,
|
|
140
|
+
index === 0 ? parserOrigin : undefined,
|
|
141
|
+
))
|
|
142
|
+
)
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
});
|
|
147
|
+
setNote(`${target.session} · terminal ${kind} queued`);
|
|
148
|
+
return true;
|
|
149
|
+
};
|
|
150
|
+
const flushRecovery = () => {
|
|
151
|
+
const waiting = recovery;
|
|
152
|
+
if (!waiting || waiting.draining) return;
|
|
153
|
+
if (!sameScope(waiting.scope, scope())) {
|
|
154
|
+
cancelRecovery();
|
|
155
|
+
setNote("terminal changed · queued input was not sent");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (generation()?.status !== "live" || !generation()?.fastLane) return;
|
|
159
|
+
waiting.draining = true;
|
|
160
|
+
const version = recoveryVersion;
|
|
161
|
+
void (async () => {
|
|
162
|
+
while (version === recoveryVersion && waiting.inputs.length > 0) {
|
|
163
|
+
const send = waiting.inputs.shift()!;
|
|
164
|
+
try {
|
|
165
|
+
if (await send()) continue;
|
|
166
|
+
} catch {
|
|
167
|
+
/* An uncertain send is never retried. */
|
|
168
|
+
}
|
|
169
|
+
if (version !== recoveryVersion) return;
|
|
170
|
+
cancelRecovery();
|
|
171
|
+
setNote("terminal unavailable · queued input was not sent");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (version === recoveryVersion) {
|
|
175
|
+
cancelRecovery();
|
|
176
|
+
setNote(null);
|
|
177
|
+
}
|
|
178
|
+
})();
|
|
179
|
+
};
|
|
32
180
|
const captureOrigin = (): boolean =>
|
|
33
181
|
Boolean(currentTuiPerformanceEventSink()?.terminalInputOrigin);
|
|
34
182
|
const flush = (): void => {
|
|
@@ -60,9 +208,14 @@ export function createApplicationTerminalInputIngress(
|
|
|
60
208
|
else setNote(`terminal unavailable · ${kind} was not sent`);
|
|
61
209
|
};
|
|
62
210
|
|
|
211
|
+
const cancelInteractionInput = () => {
|
|
212
|
+
cancelRecovery();
|
|
213
|
+
interaction.cancelPendingInput();
|
|
214
|
+
};
|
|
63
215
|
return {
|
|
64
216
|
wrapStarter(starter: GenerationStarter): GenerationStarter {
|
|
65
217
|
const start = async (...args: Parameters<GenerationStarter>) => {
|
|
218
|
+
cancelInteractionInput();
|
|
66
219
|
const [sessionName] = args;
|
|
67
220
|
const identity = pending.begin(sessionName);
|
|
68
221
|
const result = await starter(...args);
|
|
@@ -76,18 +229,22 @@ export function createApplicationTerminalInputIngress(
|
|
|
76
229
|
};
|
|
77
230
|
return Object.assign(start, {
|
|
78
231
|
cancel() {
|
|
232
|
+
cancelInteractionInput();
|
|
79
233
|
pending.dispose();
|
|
80
234
|
starter.cancel();
|
|
81
235
|
},
|
|
82
236
|
});
|
|
83
237
|
},
|
|
84
|
-
adopt(): void {
|
|
238
|
+
adopt(terminalsActive = true): void {
|
|
239
|
+
if (!terminalsActive) cancelInteractionInput();
|
|
85
240
|
flush();
|
|
241
|
+
flushRecovery();
|
|
86
242
|
},
|
|
87
243
|
routeKey(event: OpenTuiKeyEvent): void {
|
|
88
244
|
const active = generation();
|
|
89
245
|
if (
|
|
90
246
|
pending.snapshot().sessionName === null &&
|
|
247
|
+
recovery === null &&
|
|
91
248
|
active?.status === "live" &&
|
|
92
249
|
active.fastLane &&
|
|
93
250
|
focusedPane()
|
|
@@ -95,7 +252,9 @@ export function createApplicationTerminalInputIngress(
|
|
|
95
252
|
sendApplicationTerminalKey(interaction, event, captureOrigin());
|
|
96
253
|
return;
|
|
97
254
|
}
|
|
98
|
-
|
|
255
|
+
const recoveryInput = terminalInputForOpenTuiKey(event);
|
|
256
|
+
if (!recoveryInput) return;
|
|
257
|
+
if (recover([recoveryInput], Buffer.from(recoveryInput.data, "utf8"), "input")) return;
|
|
99
258
|
const copy = { name: event.name, ctrl: event.ctrl, meta: event.meta, shift: event.shift };
|
|
100
259
|
noteAdmission(
|
|
101
260
|
pending.enqueue(
|
|
@@ -117,6 +276,7 @@ export function createApplicationTerminalInputIngress(
|
|
|
117
276
|
const active = generation();
|
|
118
277
|
if (
|
|
119
278
|
pending.snapshot().sessionName === null &&
|
|
279
|
+
recovery === null &&
|
|
120
280
|
active?.status === "live" &&
|
|
121
281
|
active.fastLane &&
|
|
122
282
|
focusedPane()
|
|
@@ -124,6 +284,8 @@ export function createApplicationTerminalInputIngress(
|
|
|
124
284
|
sendApplicationTerminalPaste(interaction, copy, captureOrigin());
|
|
125
285
|
return;
|
|
126
286
|
}
|
|
287
|
+
if (recover(terminalInputsForPaste(Buffer.from(copy).toString("utf8")), copy, "paste"))
|
|
288
|
+
return;
|
|
127
289
|
noteAdmission(
|
|
128
290
|
pending.enqueue(
|
|
129
291
|
() => sendApplicationTerminalPaste(interaction, copy, captureOrigin()),
|
|
@@ -133,6 +295,7 @@ export function createApplicationTerminalInputIngress(
|
|
|
133
295
|
);
|
|
134
296
|
},
|
|
135
297
|
dispose(): void {
|
|
298
|
+
cancelInteractionInput();
|
|
136
299
|
pending.dispose();
|
|
137
300
|
},
|
|
138
301
|
};
|
package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts
CHANGED
|
@@ -79,6 +79,7 @@ export interface ApplicationTerminalInteractionControllerOptions {
|
|
|
79
79
|
|
|
80
80
|
export interface ApplicationTerminalInteractionController {
|
|
81
81
|
adoptGeneration(snapshot: OpenTuiGenerationHostSnapshot | null): void;
|
|
82
|
+
cancelPendingInput(): void;
|
|
82
83
|
adoptLayout(snapshot: OpenTuiWorkspaceLayoutSnapshot): void;
|
|
83
84
|
selectPane(paneId: string): void;
|
|
84
85
|
selectWindowLink(target: WindowLinkTarget): Promise<void>;
|
|
@@ -173,7 +174,9 @@ export function createApplicationTerminalInteractionController(
|
|
|
173
174
|
// Critical diagnostic retention still cannot own semantic interaction.
|
|
174
175
|
}
|
|
175
176
|
};
|
|
176
|
-
|
|
177
|
+
type PendingWindowSwitch = {
|
|
178
|
+
readonly selectionKind: "pane" | "window-link";
|
|
179
|
+
readonly windowLink?: WindowLinkTarget;
|
|
177
180
|
readonly traceId: string;
|
|
178
181
|
readonly target: string;
|
|
179
182
|
readonly paneId: string;
|
|
@@ -197,7 +200,8 @@ export function createApplicationTerminalInteractionController(
|
|
|
197
200
|
layoutAtMicros: number | null;
|
|
198
201
|
presentationAtMicros: number | null;
|
|
199
202
|
};
|
|
200
|
-
}
|
|
203
|
+
};
|
|
204
|
+
let pendingWindowSwitch: PendingWindowSwitch | null = null;
|
|
201
205
|
let pendingWindowRename: {
|
|
202
206
|
readonly traceId: string;
|
|
203
207
|
readonly target: string;
|
|
@@ -311,6 +315,70 @@ export function createApplicationTerminalInteractionController(
|
|
|
311
315
|
adapter: NonNullable<OpenTuiGenerationHostSnapshot["adapter"]>;
|
|
312
316
|
}> | null = null;
|
|
313
317
|
|
|
318
|
+
// Pane selection belongs to the control connection, which can outlive a
|
|
319
|
+
// terminal renderer rebuild. Never carry selection or input across reconnects.
|
|
320
|
+
const controlIdentity = (snapshot: OpenTuiGenerationHostSnapshot | null) => {
|
|
321
|
+
if (
|
|
322
|
+
!snapshot ||
|
|
323
|
+
!["live", "rebinding"].includes(snapshot.status) ||
|
|
324
|
+
!snapshot.connection ||
|
|
325
|
+
!snapshot.client ||
|
|
326
|
+
!snapshot.daemonGeneration
|
|
327
|
+
)
|
|
328
|
+
return null;
|
|
329
|
+
try {
|
|
330
|
+
const generation = snapshot.client.getSnapshot().generation;
|
|
331
|
+
if (!Number.isSafeInteger(generation)) return null;
|
|
332
|
+
return {
|
|
333
|
+
connection: snapshot.connection,
|
|
334
|
+
client: snapshot.client,
|
|
335
|
+
daemon: snapshot.daemonGeneration,
|
|
336
|
+
generation,
|
|
337
|
+
workspace: snapshot.connection.workspaceName,
|
|
338
|
+
};
|
|
339
|
+
} catch {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
type ControlIdentity = NonNullable<ReturnType<typeof controlIdentity>>;
|
|
344
|
+
const sameControl = (left: ControlIdentity | null, right: ControlIdentity | null) =>
|
|
345
|
+
left !== null &&
|
|
346
|
+
right !== null &&
|
|
347
|
+
left.connection === right.connection &&
|
|
348
|
+
left.client === right.client &&
|
|
349
|
+
left.daemon === right.daemon &&
|
|
350
|
+
left.generation === right.generation &&
|
|
351
|
+
left.workspace === right.workspace;
|
|
352
|
+
let selectedControl: ControlIdentity | null = null;
|
|
353
|
+
const waitingInput = new Set<{
|
|
354
|
+
control: ControlIdentity;
|
|
355
|
+
weight: number;
|
|
356
|
+
settle: (ready: boolean) => void;
|
|
357
|
+
}>();
|
|
358
|
+
let waitingInputBytes = 0;
|
|
359
|
+
let releasingInput: Promise<void> | null = null;
|
|
360
|
+
const awaitInputRuntime = (weight: number): Promise<boolean> => {
|
|
361
|
+
const control = controlIdentity(options.generation());
|
|
362
|
+
if (!control || waitingInput.size >= 64 || waitingInputBytes + weight > 1024 * 1024)
|
|
363
|
+
return Promise.resolve(false);
|
|
364
|
+
return new Promise((resolve) => {
|
|
365
|
+
const entry = {
|
|
366
|
+
control,
|
|
367
|
+
weight,
|
|
368
|
+
settle: (ready: boolean) => {
|
|
369
|
+
if (!waitingInput.delete(entry)) return;
|
|
370
|
+
waitingInputBytes -= weight;
|
|
371
|
+
clearTimeout(timer);
|
|
372
|
+
resolve(ready);
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
const timer = setTimeout(() => entry.settle(false), 5000);
|
|
376
|
+
timer.unref?.();
|
|
377
|
+
waitingInput.add(entry);
|
|
378
|
+
waitingInputBytes += weight;
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
|
|
314
382
|
const armDiagnosticWindowFrame = (pending: DiagnosticWindowFrameContext): void => {
|
|
315
383
|
diagnosticWindowFrame = pending;
|
|
316
384
|
diagnosticWindowFrameExpiresAtMicros = pending.startedAtMicros + 6_000_000;
|
|
@@ -633,11 +701,78 @@ export function createApplicationTerminalInteractionController(
|
|
|
633
701
|
}
|
|
634
702
|
};
|
|
635
703
|
|
|
704
|
+
const beginWindowSwitch = (pane: string, target: string, windowLink?: WindowLinkTarget): void => {
|
|
705
|
+
pendingWindowSwitch = null;
|
|
706
|
+
if (options.diagnosticsEnabled) {
|
|
707
|
+
try {
|
|
708
|
+
const active = options.generation();
|
|
709
|
+
const identity = active?.adapter?.paneCanonicalIdentity(pane);
|
|
710
|
+
const clientGeneration = active?.client?.getSnapshot().generation;
|
|
711
|
+
const startedAtMicros = diagnosticNowMicros();
|
|
712
|
+
if (
|
|
713
|
+
active?.status === "live" &&
|
|
714
|
+
active.daemonGeneration &&
|
|
715
|
+
identity &&
|
|
716
|
+
Number.isSafeInteger(clientGeneration) &&
|
|
717
|
+
startedAtMicros !== null
|
|
718
|
+
) {
|
|
719
|
+
pendingWindowSwitch = {
|
|
720
|
+
selectionKind: windowLink ? "window-link" : "pane",
|
|
721
|
+
...(windowLink ? { windowLink } : {}),
|
|
722
|
+
traceId: createTraceId(),
|
|
723
|
+
target,
|
|
724
|
+
paneId: pane,
|
|
725
|
+
startedAtMicros,
|
|
726
|
+
daemonGeneration: active.daemonGeneration,
|
|
727
|
+
clientGeneration: clientGeneration!,
|
|
728
|
+
rendererEpoch: active.rendererEpoch,
|
|
729
|
+
sourceEpoch: identity.sourceEpoch,
|
|
730
|
+
generation: identity.generation,
|
|
731
|
+
incarnation: identity.incarnation,
|
|
732
|
+
revision: identity.revision,
|
|
733
|
+
stateHash: identity.stateHash,
|
|
734
|
+
cols: identity.cols,
|
|
735
|
+
rows: identity.rows,
|
|
736
|
+
layoutPublished: false,
|
|
737
|
+
selectionApplied: false,
|
|
738
|
+
presentationPublished: false,
|
|
739
|
+
followUpRequested: false,
|
|
740
|
+
...(options.detailedWindowSwitchTiming
|
|
741
|
+
? {
|
|
742
|
+
timing: {
|
|
743
|
+
receiptAtMicros: null,
|
|
744
|
+
layoutAtMicros: null,
|
|
745
|
+
presentationAtMicros: null,
|
|
746
|
+
},
|
|
747
|
+
}
|
|
748
|
+
: {}),
|
|
749
|
+
};
|
|
750
|
+
armDiagnosticWindowFrame({ kind: "window-switch", ...pendingWindowSwitch });
|
|
751
|
+
diagnose("window-switch-start", { ...pendingWindowSwitch });
|
|
752
|
+
} else {
|
|
753
|
+
diagnose("window-switch-untracked", {
|
|
754
|
+
paneId: pane,
|
|
755
|
+
target,
|
|
756
|
+
status: active?.status ?? null,
|
|
757
|
+
daemonGeneration: active?.daemonGeneration ?? null,
|
|
758
|
+
rendererEpoch: active?.rendererEpoch ?? null,
|
|
759
|
+
clientGeneration: clientGeneration ?? null,
|
|
760
|
+
identity: identity ?? null,
|
|
761
|
+
laneCounters: active?.fastLane?.lane.counters() ?? null,
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
} catch {
|
|
765
|
+
pendingWindowSwitch = null;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
|
|
636
770
|
let linkSelection: {
|
|
637
771
|
target: WindowLinkTarget;
|
|
638
772
|
paneId: string;
|
|
639
773
|
layout: OpenTuiWorkspaceLayoutSnapshot;
|
|
640
774
|
received: boolean;
|
|
775
|
+
diagnostic: PendingWindowSwitch | null;
|
|
641
776
|
resolve: (selected: boolean) => void;
|
|
642
777
|
timer: ReturnType<typeof setTimeout>;
|
|
643
778
|
} | null = null;
|
|
@@ -646,6 +781,19 @@ export function createApplicationTerminalInteractionController(
|
|
|
646
781
|
if (!pending) return;
|
|
647
782
|
linkSelection = null;
|
|
648
783
|
clearTimeout(pending.timer);
|
|
784
|
+
const diagnostic = pending.diagnostic;
|
|
785
|
+
if (diagnostic && pendingWindowSwitch === diagnostic) {
|
|
786
|
+
if (selected) {
|
|
787
|
+
diagnostic.selectionApplied = true;
|
|
788
|
+
maybeRequestWindowSwitchFrame();
|
|
789
|
+
} else {
|
|
790
|
+
diagnoseCritical(`window-switch:${diagnostic.traceId}:failed`, "window-switch-failed", {
|
|
791
|
+
...diagnostic,
|
|
792
|
+
reason: "window-link-selection-not-applied",
|
|
793
|
+
});
|
|
794
|
+
pendingWindowSwitch = null;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
649
797
|
pending.resolve(selected);
|
|
650
798
|
};
|
|
651
799
|
const reconcileLinkSelection = (snapshot: OpenTuiWorkspaceLayoutSnapshot) => {
|
|
@@ -678,13 +826,19 @@ export function createApplicationTerminalInteractionController(
|
|
|
678
826
|
select: async (paneId) => {
|
|
679
827
|
finishLinkSelection(false);
|
|
680
828
|
const expected = liveSelectionTarget();
|
|
829
|
+
const selectionControl = controlIdentity(options.generation());
|
|
681
830
|
const selecting = pendingWindowSwitch;
|
|
682
831
|
const operationId = selecting?.traceId;
|
|
683
832
|
const selectionFailure: { current: PaneSelectionFailure | null } = { current: null };
|
|
684
833
|
const receipt = expected
|
|
685
834
|
? await selectTerminalPane(
|
|
686
835
|
expected,
|
|
687
|
-
|
|
836
|
+
() => {
|
|
837
|
+
if (!selectionControl) return liveSelectionTarget();
|
|
838
|
+
return sameControl(selectionControl, controlIdentity(options.generation()))
|
|
839
|
+
? expected
|
|
840
|
+
: null;
|
|
841
|
+
},
|
|
688
842
|
paneId,
|
|
689
843
|
operationId,
|
|
690
844
|
(failure) => {
|
|
@@ -757,6 +911,14 @@ export function createApplicationTerminalInteractionController(
|
|
|
757
911
|
},
|
|
758
912
|
send: async (paneId, routed) => {
|
|
759
913
|
const { input, parserOrigin } = routed;
|
|
914
|
+
const selectionVersion = paneInput.selectionVersion;
|
|
915
|
+
if (options.generation()?.status === "rebinding") {
|
|
916
|
+
if (!(await awaitInputRuntime(Buffer.byteLength(input.data, "utf8")))) return false;
|
|
917
|
+
} else if (releasingInput) {
|
|
918
|
+
await releasingInput;
|
|
919
|
+
}
|
|
920
|
+
if (paneInput.selectionVersion !== selectionVersion || paneInput.focusedPane !== paneId)
|
|
921
|
+
return false;
|
|
760
922
|
const active = options.generation();
|
|
761
923
|
if (active?.status !== "live" || !active.fastLane) return false;
|
|
762
924
|
let fixtureEnabled: boolean;
|
|
@@ -838,7 +1000,31 @@ export function createApplicationTerminalInteractionController(
|
|
|
838
1000
|
});
|
|
839
1001
|
|
|
840
1002
|
return {
|
|
1003
|
+
cancelPendingInput() {
|
|
1004
|
+
finishLinkSelection(false);
|
|
1005
|
+
paneInput.invalidateSelection();
|
|
1006
|
+
for (const entry of waitingInput) entry.settle(false);
|
|
1007
|
+
},
|
|
841
1008
|
adoptGeneration(snapshot) {
|
|
1009
|
+
const control = controlIdentity(snapshot);
|
|
1010
|
+
if (!sameControl(selectedControl, control)) {
|
|
1011
|
+
finishLinkSelection(false);
|
|
1012
|
+
paneInput.invalidateSelection();
|
|
1013
|
+
}
|
|
1014
|
+
selectedControl = control;
|
|
1015
|
+
if (waitingInput.size > 0 && snapshot?.status === "live" && snapshot.fastLane) {
|
|
1016
|
+
// Old waiters resume first; input admitted synchronously by the next
|
|
1017
|
+
// layout publication must not overtake them in this microtask turn.
|
|
1018
|
+
const barrier = Promise.resolve();
|
|
1019
|
+
releasingInput = barrier;
|
|
1020
|
+
queueMicrotask(() => {
|
|
1021
|
+
if (releasingInput === barrier) releasingInput = null;
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
for (const entry of waitingInput) {
|
|
1025
|
+
if (!sameControl(entry.control, control)) entry.settle(false);
|
|
1026
|
+
else if (snapshot?.status === "live" && snapshot.fastLane) entry.settle(true);
|
|
1027
|
+
}
|
|
842
1028
|
let next: typeof inputAuthorityIdentity = null;
|
|
843
1029
|
if (
|
|
844
1030
|
snapshot?.status === "live" &&
|
|
@@ -876,7 +1062,6 @@ export function createApplicationTerminalInteractionController(
|
|
|
876
1062
|
next.adapter !== inputAuthorityIdentity.adapter);
|
|
877
1063
|
if (next === null || replaced) {
|
|
878
1064
|
finishLinkSelection(false);
|
|
879
|
-
paneInput.invalidateSelection();
|
|
880
1065
|
pendingWindowSwitch = null;
|
|
881
1066
|
pendingWindowRename = null;
|
|
882
1067
|
diagnosticWindowFrame = null;
|
|
@@ -1035,6 +1220,7 @@ export function createApplicationTerminalInteractionController(
|
|
|
1035
1220
|
)
|
|
1036
1221
|
return;
|
|
1037
1222
|
finishLinkSelection(false);
|
|
1223
|
+
beginWindowSwitch(paneId, target.expectedSemanticWindowId, target);
|
|
1038
1224
|
let resolve!: (selected: boolean) => void;
|
|
1039
1225
|
const settled = new Promise<boolean>((done) => {
|
|
1040
1226
|
resolve = done;
|
|
@@ -1044,6 +1230,7 @@ export function createApplicationTerminalInteractionController(
|
|
|
1044
1230
|
paneId,
|
|
1045
1231
|
layout: snapshot,
|
|
1046
1232
|
received: false,
|
|
1233
|
+
diagnostic: pendingWindowSwitch,
|
|
1047
1234
|
resolve,
|
|
1048
1235
|
timer: setTimeout(() => {
|
|
1049
1236
|
if (linkSelection === pending) finishLinkSelection(false);
|
|
@@ -1072,6 +1259,7 @@ export function createApplicationTerminalInteractionController(
|
|
|
1072
1259
|
}
|
|
1073
1260
|
const wrapper = (await expected.client.dispatch({
|
|
1074
1261
|
kind: "semantic-intent",
|
|
1262
|
+
...(pending.diagnostic ? { operationId: pending.diagnostic.traceId } : {}),
|
|
1075
1263
|
intent: {
|
|
1076
1264
|
verb: "workspace.window.link.select",
|
|
1077
1265
|
workspaceName: expected.workspaceName,
|
|
@@ -1084,6 +1272,7 @@ export function createApplicationTerminalInteractionController(
|
|
|
1084
1272
|
wrapper.kind !== "semantic-intent" ||
|
|
1085
1273
|
!result.success ||
|
|
1086
1274
|
result.data.operationId !== wrapper.operationId ||
|
|
1275
|
+
(pending.diagnostic && wrapper.operationId !== pending.diagnostic.traceId) ||
|
|
1087
1276
|
result.data.daemonInstanceId !== expected.daemonGeneration ||
|
|
1088
1277
|
result.data.workspaceName !== expected.workspaceName ||
|
|
1089
1278
|
result.data.target.linkId !== target.linkId ||
|
|
@@ -1094,6 +1283,18 @@ export function createApplicationTerminalInteractionController(
|
|
|
1094
1283
|
finishLinkSelection(false);
|
|
1095
1284
|
return;
|
|
1096
1285
|
}
|
|
1286
|
+
if (pending.diagnostic) {
|
|
1287
|
+
const receiptAtMicros = diagnosticNowMicros();
|
|
1288
|
+
if (pending.diagnostic.timing)
|
|
1289
|
+
pending.diagnostic.timing.receiptAtMicros = receiptAtMicros;
|
|
1290
|
+
diagnose("window-switch-receipt", {
|
|
1291
|
+
...pending.diagnostic,
|
|
1292
|
+
operationId: wrapper.operationId,
|
|
1293
|
+
selected: true,
|
|
1294
|
+
applied: true,
|
|
1295
|
+
...(options.detailedWindowSwitchTiming ? { phaseAtMicros: receiptAtMicros } : {}),
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1097
1298
|
pending.received = true;
|
|
1098
1299
|
reconcileLinkSelection(pending.layout);
|
|
1099
1300
|
await settled;
|
|
@@ -1335,55 +1536,7 @@ export function createApplicationTerminalInteractionController(
|
|
|
1335
1536
|
const pane = next ? paneForWindow(next) : null;
|
|
1336
1537
|
const target = next?.semanticWindowId ?? next?.windowName;
|
|
1337
1538
|
if (!pane || !target) return;
|
|
1338
|
-
|
|
1339
|
-
try {
|
|
1340
|
-
const active = options.generation();
|
|
1341
|
-
const identity = active?.adapter?.paneCanonicalIdentity(pane);
|
|
1342
|
-
const clientGeneration = active?.client?.getSnapshot().generation;
|
|
1343
|
-
const startedAtMicros = diagnosticNowMicros();
|
|
1344
|
-
if (
|
|
1345
|
-
active?.status === "live" &&
|
|
1346
|
-
active.daemonGeneration &&
|
|
1347
|
-
identity &&
|
|
1348
|
-
Number.isSafeInteger(clientGeneration) &&
|
|
1349
|
-
startedAtMicros !== null
|
|
1350
|
-
) {
|
|
1351
|
-
pendingWindowSwitch = {
|
|
1352
|
-
traceId: createTraceId(),
|
|
1353
|
-
target,
|
|
1354
|
-
paneId: pane,
|
|
1355
|
-
startedAtMicros,
|
|
1356
|
-
daemonGeneration: active.daemonGeneration,
|
|
1357
|
-
clientGeneration: clientGeneration!,
|
|
1358
|
-
rendererEpoch: active.rendererEpoch,
|
|
1359
|
-
sourceEpoch: identity.sourceEpoch,
|
|
1360
|
-
generation: identity.generation,
|
|
1361
|
-
incarnation: identity.incarnation,
|
|
1362
|
-
revision: identity.revision,
|
|
1363
|
-
stateHash: identity.stateHash,
|
|
1364
|
-
cols: identity.cols,
|
|
1365
|
-
rows: identity.rows,
|
|
1366
|
-
layoutPublished: false,
|
|
1367
|
-
selectionApplied: false,
|
|
1368
|
-
presentationPublished: false,
|
|
1369
|
-
followUpRequested: false,
|
|
1370
|
-
...(options.detailedWindowSwitchTiming
|
|
1371
|
-
? {
|
|
1372
|
-
timing: {
|
|
1373
|
-
receiptAtMicros: null,
|
|
1374
|
-
layoutAtMicros: null,
|
|
1375
|
-
presentationAtMicros: null,
|
|
1376
|
-
},
|
|
1377
|
-
}
|
|
1378
|
-
: {}),
|
|
1379
|
-
};
|
|
1380
|
-
armDiagnosticWindowFrame({ kind: "window-switch", ...pendingWindowSwitch });
|
|
1381
|
-
diagnose("window-switch-start", { ...pendingWindowSwitch });
|
|
1382
|
-
}
|
|
1383
|
-
} catch {
|
|
1384
|
-
pendingWindowSwitch = null;
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1539
|
+
beginWindowSwitch(pane, target);
|
|
1387
1540
|
paneInput.selectPane(pane, { presentOptimistically: false });
|
|
1388
1541
|
},
|
|
1389
1542
|
async zoomPane(targetPane) {
|
|
@@ -1583,6 +1736,7 @@ export function createApplicationTerminalInteractionController(
|
|
|
1583
1736
|
? {}
|
|
1584
1737
|
: { phaseAtMicros: pending.timing.presentationAtMicros }),
|
|
1585
1738
|
});
|
|
1739
|
+
maybeRequestWindowSwitchFrame();
|
|
1586
1740
|
},
|
|
1587
1741
|
observeDiagnosticWindowFrame() {
|
|
1588
1742
|
if (!options.diagnosticsEnabled) return null;
|