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
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { resolveRuntimeNamespace } from "../../../lib/runtime-namespace.js";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { SavedMachineSchema } from "@tmux-ide/contracts";
|
|
4
|
-
import { loadSavedMachines } from "../../../lib/saved-machines.js";
|
|
4
|
+
import { loadSavedMachines, watchSavedMachines } from "../../../lib/saved-machines.js";
|
|
5
5
|
import { applicationMachineAuthorityManager } from "./application-machine-authority.js";
|
|
6
|
+
import { tuiPerfMark } from "./application-performance-log.js";
|
|
6
7
|
/** Labels describe access routes; they never become routing identities. */
|
|
7
8
|
export function ephemeralMachineProfile(alias, existing) {
|
|
8
9
|
const target = SavedMachineSchema.shape.sshTarget.parse(alias);
|
|
@@ -20,17 +21,45 @@ export function ephemeralMachineProfile(alias, existing) {
|
|
|
20
21
|
}
|
|
21
22
|
/** Local discovery and rendering never wait for any remote handshake. */
|
|
22
23
|
export function initializeApplicationMachines(aliases) {
|
|
23
|
-
const
|
|
24
|
+
const development = resolveRuntimeNamespace().development;
|
|
25
|
+
const profiles = development ? [] : [...loadSavedMachines().machines];
|
|
26
|
+
const ephemeral = [];
|
|
24
27
|
let preferred = null;
|
|
25
28
|
for (const alias of aliases) {
|
|
26
29
|
let profile = profiles.find((p) => p.sshTarget === alias && p.enabled);
|
|
27
30
|
if (!profile) {
|
|
28
31
|
profile = ephemeralMachineProfile(alias, profiles);
|
|
29
32
|
profiles.push(profile);
|
|
33
|
+
ephemeral.push(profile);
|
|
30
34
|
}
|
|
31
35
|
preferred ??= profile.id;
|
|
32
36
|
}
|
|
33
37
|
applicationMachineAuthorityManager.initialize(profiles);
|
|
34
38
|
if (preferred)
|
|
35
39
|
applicationMachineAuthorityManager.select(preferred);
|
|
40
|
+
if (!development) {
|
|
41
|
+
const failed = () => tuiPerfMark("machine-registry-error", { reason: "registry-unavailable-or-invalid" });
|
|
42
|
+
try {
|
|
43
|
+
applicationMachineAuthorityManager.followProfiles((onChange) => watchSavedMachines((registry) => {
|
|
44
|
+
const next = [...registry.machines];
|
|
45
|
+
for (let index = 0; index < ephemeral.length; index++) {
|
|
46
|
+
let profile = ephemeral[index];
|
|
47
|
+
// A newly saved route may claim an ephemeral display label. Keep the
|
|
48
|
+
// explicit connection's identity while making the label unambiguous.
|
|
49
|
+
if (next.some((entry) => entry.label.normalize("NFKC").toLowerCase() ===
|
|
50
|
+
profile.label.normalize("NFKC").toLowerCase()))
|
|
51
|
+
profile = {
|
|
52
|
+
...profile,
|
|
53
|
+
label: ephemeralMachineProfile(profile.sshTarget, next).label,
|
|
54
|
+
};
|
|
55
|
+
ephemeral[index] = profile;
|
|
56
|
+
next.push(profile);
|
|
57
|
+
}
|
|
58
|
+
onChange(next);
|
|
59
|
+
}, failed));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
failed();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
36
65
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createMemo, createSignal } from "solid-js";
|
|
1
|
+
import { createMemo, createSignal, untrack } from "solid-js";
|
|
2
2
|
import { applicationCommandDescription, filterApplicationCommands, } from "../workspace/application-command-description.js";
|
|
3
3
|
import { applicationPaletteKeyboardDisposition, } from "./application-palette-input.js";
|
|
4
4
|
import { applicationPaneRenameKeyAction, applicationPaneRenamePaste, } from "./application-pane-rename-input.js";
|
|
@@ -11,9 +11,13 @@ export function createApplicationPaletteSearchOwner(options) {
|
|
|
11
11
|
const [help, setHelp] = createSignal(false);
|
|
12
12
|
const [query, setQuery] = createSignal("");
|
|
13
13
|
const [selectedId, setSelectedId] = createSignal(null);
|
|
14
|
-
const
|
|
14
|
+
const readCommands = () => filterApplicationCommands(options
|
|
15
15
|
.commands()
|
|
16
|
-
.filter((c) => !localOnly() || typeof c === "string" || !c.fleet || c.fleet.machineId === "local"), query())
|
|
16
|
+
.filter((c) => !localOnly() || typeof c === "string" || !c.fleet || c.fleet.machineId === "local"), query());
|
|
17
|
+
// Closed overlays must not subscribe to catalog churn. Explicit reads still
|
|
18
|
+
// resolve current commands, including reset() before the open action commits.
|
|
19
|
+
const activeCommands = createMemo(() => (options.open() ? readCommands() : null));
|
|
20
|
+
const commands = () => activeCommands() ?? untrack(readCommands);
|
|
17
21
|
const selection = () => Math.max(0, commands().findIndex((command) => applicationCommandDescription(command).id === selectedId()));
|
|
18
22
|
const select = (index) => {
|
|
19
23
|
const command = commands()[index];
|
|
@@ -29,6 +29,8 @@ stream?.on("drain", drain);
|
|
|
29
29
|
export const tuiLifecycleStream = stream ? Object.freeze({ enabled: true }) : null;
|
|
30
30
|
export const tuiPerfStream = TUI_PERF_LOG ? tuiLifecycleStream : null;
|
|
31
31
|
const LIFECYCLE_PHASES = new Set([
|
|
32
|
+
"machine-connection-status",
|
|
33
|
+
"machine-registry-error",
|
|
32
34
|
"generation-status",
|
|
33
35
|
"generation-connection-start",
|
|
34
36
|
"generation-connection-resolved",
|
|
@@ -132,6 +134,7 @@ export function closeTuiPerfMarks() {
|
|
|
132
134
|
closePromise = (async () => {
|
|
133
135
|
if (!stream)
|
|
134
136
|
return;
|
|
137
|
+
await writer?.flush();
|
|
135
138
|
await flushTuiPerfMarks();
|
|
136
139
|
// No drain callback may enqueue another critical record after end().
|
|
137
140
|
stream.off("drain", drain);
|
|
@@ -7,6 +7,133 @@ import { terminalInputForOpenTuiKey, terminalInputsForPaste, } from "./terminal-
|
|
|
7
7
|
export function createApplicationTerminalInputIngress(interaction, generation, sessionOwner, focusedPane, setNote) {
|
|
8
8
|
const pending = createApplicationPendingTerminalInputOwner();
|
|
9
9
|
let ownsNote = false;
|
|
10
|
+
let recovery = null;
|
|
11
|
+
const scope = () => {
|
|
12
|
+
const active = generation();
|
|
13
|
+
const owner = sessionOwner();
|
|
14
|
+
const pane = focusedPane();
|
|
15
|
+
const session = owner?.sessionName();
|
|
16
|
+
if (!active ||
|
|
17
|
+
!["live", "rebinding"].includes(active.status) ||
|
|
18
|
+
!active.connection ||
|
|
19
|
+
!active.client ||
|
|
20
|
+
!active.daemonGeneration ||
|
|
21
|
+
!owner ||
|
|
22
|
+
!session ||
|
|
23
|
+
!pane)
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
const clientGeneration = active.client.getSnapshot().generation;
|
|
27
|
+
if (!Number.isSafeInteger(clientGeneration))
|
|
28
|
+
return null;
|
|
29
|
+
return {
|
|
30
|
+
owner,
|
|
31
|
+
connection: active.connection,
|
|
32
|
+
client: active.client,
|
|
33
|
+
daemon: active.daemonGeneration,
|
|
34
|
+
clientGeneration: clientGeneration,
|
|
35
|
+
session: session,
|
|
36
|
+
pane,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const sameScope = (left, right) => right !== null &&
|
|
44
|
+
left.owner === right.owner &&
|
|
45
|
+
left.connection === right.connection &&
|
|
46
|
+
left.client === right.client &&
|
|
47
|
+
left.daemon === right.daemon &&
|
|
48
|
+
left.clientGeneration === right.clientGeneration &&
|
|
49
|
+
left.session === right.session &&
|
|
50
|
+
left.pane === right.pane;
|
|
51
|
+
let recoveryVersion = 0;
|
|
52
|
+
const cancelRecovery = () => {
|
|
53
|
+
recoveryVersion++;
|
|
54
|
+
if (recovery)
|
|
55
|
+
clearTimeout(recovery.timer);
|
|
56
|
+
recovery = null;
|
|
57
|
+
};
|
|
58
|
+
const recover = (inputs, bytes, kind) => {
|
|
59
|
+
if (pending.snapshot().sessionName !== null ||
|
|
60
|
+
(!recovery && generation()?.status !== "rebinding"))
|
|
61
|
+
return false;
|
|
62
|
+
const target = scope();
|
|
63
|
+
if (!target)
|
|
64
|
+
return false;
|
|
65
|
+
if (recovery && !sameScope(recovery.scope, target))
|
|
66
|
+
cancelRecovery();
|
|
67
|
+
if (!recovery) {
|
|
68
|
+
const timer = setTimeout(() => {
|
|
69
|
+
cancelRecovery();
|
|
70
|
+
setNote("terminal recovery timed out · queued input was not sent");
|
|
71
|
+
}, 5_000);
|
|
72
|
+
timer.unref?.();
|
|
73
|
+
recovery = { scope: target, inputs: [], weight: 0, admitted: 0, draining: false, timer };
|
|
74
|
+
}
|
|
75
|
+
const weight = bytes.byteLength;
|
|
76
|
+
if (recovery.admitted >= 64 || recovery.weight + weight > 1024 * 1024) {
|
|
77
|
+
setNote("terminal input queue full · wait for the session to connect");
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
recovery.weight += weight;
|
|
81
|
+
recovery.admitted++;
|
|
82
|
+
const version = recoveryVersion;
|
|
83
|
+
const parserOrigin = captureOrigin()
|
|
84
|
+
? {
|
|
85
|
+
origin: kind === "paste" ? "bracketed-paste" : "keyboard",
|
|
86
|
+
payload: Buffer.from(bytes),
|
|
87
|
+
}
|
|
88
|
+
: undefined;
|
|
89
|
+
recovery.inputs.push(async () => {
|
|
90
|
+
for (const [index, input] of inputs.entries()) {
|
|
91
|
+
if (version !== recoveryVersion ||
|
|
92
|
+
!sameScope(target, scope()) ||
|
|
93
|
+
generation()?.status !== "live" ||
|
|
94
|
+
!(await interaction.sendInputToPane(target.pane, input, index === 0 ? parserOrigin : undefined)))
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
});
|
|
99
|
+
setNote(`${target.session} · terminal ${kind} queued`);
|
|
100
|
+
return true;
|
|
101
|
+
};
|
|
102
|
+
const flushRecovery = () => {
|
|
103
|
+
const waiting = recovery;
|
|
104
|
+
if (!waiting || waiting.draining)
|
|
105
|
+
return;
|
|
106
|
+
if (!sameScope(waiting.scope, scope())) {
|
|
107
|
+
cancelRecovery();
|
|
108
|
+
setNote("terminal changed · queued input was not sent");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (generation()?.status !== "live" || !generation()?.fastLane)
|
|
112
|
+
return;
|
|
113
|
+
waiting.draining = true;
|
|
114
|
+
const version = recoveryVersion;
|
|
115
|
+
void (async () => {
|
|
116
|
+
while (version === recoveryVersion && waiting.inputs.length > 0) {
|
|
117
|
+
const send = waiting.inputs.shift();
|
|
118
|
+
try {
|
|
119
|
+
if (await send())
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
/* An uncertain send is never retried. */
|
|
124
|
+
}
|
|
125
|
+
if (version !== recoveryVersion)
|
|
126
|
+
return;
|
|
127
|
+
cancelRecovery();
|
|
128
|
+
setNote("terminal unavailable · queued input was not sent");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (version === recoveryVersion) {
|
|
132
|
+
cancelRecovery();
|
|
133
|
+
setNote(null);
|
|
134
|
+
}
|
|
135
|
+
})();
|
|
136
|
+
};
|
|
10
137
|
const captureOrigin = () => Boolean(currentTuiPerformanceEventSink()?.terminalInputOrigin);
|
|
11
138
|
const flush = () => {
|
|
12
139
|
const owner = sessionOwner();
|
|
@@ -36,9 +163,14 @@ export function createApplicationTerminalInputIngress(interaction, generation, s
|
|
|
36
163
|
else
|
|
37
164
|
setNote(`terminal unavailable · ${kind} was not sent`);
|
|
38
165
|
};
|
|
166
|
+
const cancelInteractionInput = () => {
|
|
167
|
+
cancelRecovery();
|
|
168
|
+
interaction.cancelPendingInput();
|
|
169
|
+
};
|
|
39
170
|
return {
|
|
40
171
|
wrapStarter(starter) {
|
|
41
172
|
const start = async (...args) => {
|
|
173
|
+
cancelInteractionInput();
|
|
42
174
|
const [sessionName] = args;
|
|
43
175
|
const identity = pending.begin(sessionName);
|
|
44
176
|
const result = await starter(...args);
|
|
@@ -52,24 +184,32 @@ export function createApplicationTerminalInputIngress(interaction, generation, s
|
|
|
52
184
|
};
|
|
53
185
|
return Object.assign(start, {
|
|
54
186
|
cancel() {
|
|
187
|
+
cancelInteractionInput();
|
|
55
188
|
pending.dispose();
|
|
56
189
|
starter.cancel();
|
|
57
190
|
},
|
|
58
191
|
});
|
|
59
192
|
},
|
|
60
|
-
adopt() {
|
|
193
|
+
adopt(terminalsActive = true) {
|
|
194
|
+
if (!terminalsActive)
|
|
195
|
+
cancelInteractionInput();
|
|
61
196
|
flush();
|
|
197
|
+
flushRecovery();
|
|
62
198
|
},
|
|
63
199
|
routeKey(event) {
|
|
64
200
|
const active = generation();
|
|
65
201
|
if (pending.snapshot().sessionName === null &&
|
|
202
|
+
recovery === null &&
|
|
66
203
|
active?.status === "live" &&
|
|
67
204
|
active.fastLane &&
|
|
68
205
|
focusedPane()) {
|
|
69
206
|
sendApplicationTerminalKey(interaction, event, captureOrigin());
|
|
70
207
|
return;
|
|
71
208
|
}
|
|
72
|
-
|
|
209
|
+
const recoveryInput = terminalInputForOpenTuiKey(event);
|
|
210
|
+
if (!recoveryInput)
|
|
211
|
+
return;
|
|
212
|
+
if (recover([recoveryInput], Buffer.from(recoveryInput.data, "utf8"), "input"))
|
|
73
213
|
return;
|
|
74
214
|
const copy = { name: event.name, ctrl: event.ctrl, meta: event.meta, shift: event.shift };
|
|
75
215
|
noteAdmission(pending.enqueue(() => sendApplicationTerminalKey(interaction, copy, captureOrigin()), Buffer.byteLength(event.name, "utf8")), "input");
|
|
@@ -87,15 +227,19 @@ export function createApplicationTerminalInputIngress(interaction, generation, s
|
|
|
87
227
|
}
|
|
88
228
|
const active = generation();
|
|
89
229
|
if (pending.snapshot().sessionName === null &&
|
|
230
|
+
recovery === null &&
|
|
90
231
|
active?.status === "live" &&
|
|
91
232
|
active.fastLane &&
|
|
92
233
|
focusedPane()) {
|
|
93
234
|
sendApplicationTerminalPaste(interaction, copy, captureOrigin());
|
|
94
235
|
return;
|
|
95
236
|
}
|
|
237
|
+
if (recover(terminalInputsForPaste(Buffer.from(copy).toString("utf8")), copy, "paste"))
|
|
238
|
+
return;
|
|
96
239
|
noteAdmission(pending.enqueue(() => sendApplicationTerminalPaste(interaction, copy, captureOrigin()), copy.byteLength), "paste");
|
|
97
240
|
},
|
|
98
241
|
dispose() {
|
|
242
|
+
cancelInteractionInput();
|
|
99
243
|
pending.dispose();
|
|
100
244
|
},
|
|
101
245
|
};
|
package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js
CHANGED
|
@@ -84,6 +84,64 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
84
84
|
let diagnosticWindowFrame = null;
|
|
85
85
|
let diagnosticWindowFrameExpiresAtMicros = 0;
|
|
86
86
|
let inputAuthorityIdentity = null;
|
|
87
|
+
// Pane selection belongs to the control connection, which can outlive a
|
|
88
|
+
// terminal renderer rebuild. Never carry selection or input across reconnects.
|
|
89
|
+
const controlIdentity = (snapshot) => {
|
|
90
|
+
if (!snapshot ||
|
|
91
|
+
!["live", "rebinding"].includes(snapshot.status) ||
|
|
92
|
+
!snapshot.connection ||
|
|
93
|
+
!snapshot.client ||
|
|
94
|
+
!snapshot.daemonGeneration)
|
|
95
|
+
return null;
|
|
96
|
+
try {
|
|
97
|
+
const generation = snapshot.client.getSnapshot().generation;
|
|
98
|
+
if (!Number.isSafeInteger(generation))
|
|
99
|
+
return null;
|
|
100
|
+
return {
|
|
101
|
+
connection: snapshot.connection,
|
|
102
|
+
client: snapshot.client,
|
|
103
|
+
daemon: snapshot.daemonGeneration,
|
|
104
|
+
generation,
|
|
105
|
+
workspace: snapshot.connection.workspaceName,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const sameControl = (left, right) => left !== null &&
|
|
113
|
+
right !== null &&
|
|
114
|
+
left.connection === right.connection &&
|
|
115
|
+
left.client === right.client &&
|
|
116
|
+
left.daemon === right.daemon &&
|
|
117
|
+
left.generation === right.generation &&
|
|
118
|
+
left.workspace === right.workspace;
|
|
119
|
+
let selectedControl = null;
|
|
120
|
+
const waitingInput = new Set();
|
|
121
|
+
let waitingInputBytes = 0;
|
|
122
|
+
let releasingInput = null;
|
|
123
|
+
const awaitInputRuntime = (weight) => {
|
|
124
|
+
const control = controlIdentity(options.generation());
|
|
125
|
+
if (!control || waitingInput.size >= 64 || waitingInputBytes + weight > 1024 * 1024)
|
|
126
|
+
return Promise.resolve(false);
|
|
127
|
+
return new Promise((resolve) => {
|
|
128
|
+
const entry = {
|
|
129
|
+
control,
|
|
130
|
+
weight,
|
|
131
|
+
settle: (ready) => {
|
|
132
|
+
if (!waitingInput.delete(entry))
|
|
133
|
+
return;
|
|
134
|
+
waitingInputBytes -= weight;
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
resolve(ready);
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
const timer = setTimeout(() => entry.settle(false), 5000);
|
|
140
|
+
timer.unref?.();
|
|
141
|
+
waitingInput.add(entry);
|
|
142
|
+
waitingInputBytes += weight;
|
|
143
|
+
});
|
|
144
|
+
};
|
|
87
145
|
const armDiagnosticWindowFrame = (pending) => {
|
|
88
146
|
diagnosticWindowFrame = pending;
|
|
89
147
|
diagnosticWindowFrameExpiresAtMicros = pending.startedAtMicros + 6_000_000;
|
|
@@ -356,6 +414,71 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
356
414
|
pendingWindowSwitch = null;
|
|
357
415
|
}
|
|
358
416
|
};
|
|
417
|
+
const beginWindowSwitch = (pane, target, windowLink) => {
|
|
418
|
+
pendingWindowSwitch = null;
|
|
419
|
+
if (options.diagnosticsEnabled) {
|
|
420
|
+
try {
|
|
421
|
+
const active = options.generation();
|
|
422
|
+
const identity = active?.adapter?.paneCanonicalIdentity(pane);
|
|
423
|
+
const clientGeneration = active?.client?.getSnapshot().generation;
|
|
424
|
+
const startedAtMicros = diagnosticNowMicros();
|
|
425
|
+
if (active?.status === "live" &&
|
|
426
|
+
active.daemonGeneration &&
|
|
427
|
+
identity &&
|
|
428
|
+
Number.isSafeInteger(clientGeneration) &&
|
|
429
|
+
startedAtMicros !== null) {
|
|
430
|
+
pendingWindowSwitch = {
|
|
431
|
+
selectionKind: windowLink ? "window-link" : "pane",
|
|
432
|
+
...(windowLink ? { windowLink } : {}),
|
|
433
|
+
traceId: createTraceId(),
|
|
434
|
+
target,
|
|
435
|
+
paneId: pane,
|
|
436
|
+
startedAtMicros,
|
|
437
|
+
daemonGeneration: active.daemonGeneration,
|
|
438
|
+
clientGeneration: clientGeneration,
|
|
439
|
+
rendererEpoch: active.rendererEpoch,
|
|
440
|
+
sourceEpoch: identity.sourceEpoch,
|
|
441
|
+
generation: identity.generation,
|
|
442
|
+
incarnation: identity.incarnation,
|
|
443
|
+
revision: identity.revision,
|
|
444
|
+
stateHash: identity.stateHash,
|
|
445
|
+
cols: identity.cols,
|
|
446
|
+
rows: identity.rows,
|
|
447
|
+
layoutPublished: false,
|
|
448
|
+
selectionApplied: false,
|
|
449
|
+
presentationPublished: false,
|
|
450
|
+
followUpRequested: false,
|
|
451
|
+
...(options.detailedWindowSwitchTiming
|
|
452
|
+
? {
|
|
453
|
+
timing: {
|
|
454
|
+
receiptAtMicros: null,
|
|
455
|
+
layoutAtMicros: null,
|
|
456
|
+
presentationAtMicros: null,
|
|
457
|
+
},
|
|
458
|
+
}
|
|
459
|
+
: {}),
|
|
460
|
+
};
|
|
461
|
+
armDiagnosticWindowFrame({ kind: "window-switch", ...pendingWindowSwitch });
|
|
462
|
+
diagnose("window-switch-start", { ...pendingWindowSwitch });
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
diagnose("window-switch-untracked", {
|
|
466
|
+
paneId: pane,
|
|
467
|
+
target,
|
|
468
|
+
status: active?.status ?? null,
|
|
469
|
+
daemonGeneration: active?.daemonGeneration ?? null,
|
|
470
|
+
rendererEpoch: active?.rendererEpoch ?? null,
|
|
471
|
+
clientGeneration: clientGeneration ?? null,
|
|
472
|
+
identity: identity ?? null,
|
|
473
|
+
laneCounters: active?.fastLane?.lane.counters() ?? null,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
pendingWindowSwitch = null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
};
|
|
359
482
|
let linkSelection = null;
|
|
360
483
|
const finishLinkSelection = (selected) => {
|
|
361
484
|
const pending = linkSelection;
|
|
@@ -363,6 +486,20 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
363
486
|
return;
|
|
364
487
|
linkSelection = null;
|
|
365
488
|
clearTimeout(pending.timer);
|
|
489
|
+
const diagnostic = pending.diagnostic;
|
|
490
|
+
if (diagnostic && pendingWindowSwitch === diagnostic) {
|
|
491
|
+
if (selected) {
|
|
492
|
+
diagnostic.selectionApplied = true;
|
|
493
|
+
maybeRequestWindowSwitchFrame();
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
diagnoseCritical(`window-switch:${diagnostic.traceId}:failed`, "window-switch-failed", {
|
|
497
|
+
...diagnostic,
|
|
498
|
+
reason: "window-link-selection-not-applied",
|
|
499
|
+
});
|
|
500
|
+
pendingWindowSwitch = null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
366
503
|
pending.resolve(selected);
|
|
367
504
|
};
|
|
368
505
|
const reconcileLinkSelection = (snapshot) => {
|
|
@@ -386,11 +523,18 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
386
523
|
select: async (paneId) => {
|
|
387
524
|
finishLinkSelection(false);
|
|
388
525
|
const expected = liveSelectionTarget();
|
|
526
|
+
const selectionControl = controlIdentity(options.generation());
|
|
389
527
|
const selecting = pendingWindowSwitch;
|
|
390
528
|
const operationId = selecting?.traceId;
|
|
391
529
|
const selectionFailure = { current: null };
|
|
392
530
|
const receipt = expected
|
|
393
|
-
? await selectTerminalPane(expected,
|
|
531
|
+
? await selectTerminalPane(expected, () => {
|
|
532
|
+
if (!selectionControl)
|
|
533
|
+
return liveSelectionTarget();
|
|
534
|
+
return sameControl(selectionControl, controlIdentity(options.generation()))
|
|
535
|
+
? expected
|
|
536
|
+
: null;
|
|
537
|
+
}, paneId, operationId, (failure) => {
|
|
394
538
|
selectionFailure.current = failure;
|
|
395
539
|
}, windowLinkForPane(options.layout(), paneId) ?? undefined)
|
|
396
540
|
: null;
|
|
@@ -455,6 +599,16 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
455
599
|
},
|
|
456
600
|
send: async (paneId, routed) => {
|
|
457
601
|
const { input, parserOrigin } = routed;
|
|
602
|
+
const selectionVersion = paneInput.selectionVersion;
|
|
603
|
+
if (options.generation()?.status === "rebinding") {
|
|
604
|
+
if (!(await awaitInputRuntime(Buffer.byteLength(input.data, "utf8"))))
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
else if (releasingInput) {
|
|
608
|
+
await releasingInput;
|
|
609
|
+
}
|
|
610
|
+
if (paneInput.selectionVersion !== selectionVersion || paneInput.focusedPane !== paneId)
|
|
611
|
+
return false;
|
|
458
612
|
const active = options.generation();
|
|
459
613
|
if (active?.status !== "live" || !active.fastLane)
|
|
460
614
|
return false;
|
|
@@ -534,7 +688,35 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
534
688
|
onFocusedPane: options.setFocusedPane,
|
|
535
689
|
});
|
|
536
690
|
return {
|
|
691
|
+
cancelPendingInput() {
|
|
692
|
+
finishLinkSelection(false);
|
|
693
|
+
paneInput.invalidateSelection();
|
|
694
|
+
for (const entry of waitingInput)
|
|
695
|
+
entry.settle(false);
|
|
696
|
+
},
|
|
537
697
|
adoptGeneration(snapshot) {
|
|
698
|
+
const control = controlIdentity(snapshot);
|
|
699
|
+
if (!sameControl(selectedControl, control)) {
|
|
700
|
+
finishLinkSelection(false);
|
|
701
|
+
paneInput.invalidateSelection();
|
|
702
|
+
}
|
|
703
|
+
selectedControl = control;
|
|
704
|
+
if (waitingInput.size > 0 && snapshot?.status === "live" && snapshot.fastLane) {
|
|
705
|
+
// Old waiters resume first; input admitted synchronously by the next
|
|
706
|
+
// layout publication must not overtake them in this microtask turn.
|
|
707
|
+
const barrier = Promise.resolve();
|
|
708
|
+
releasingInput = barrier;
|
|
709
|
+
queueMicrotask(() => {
|
|
710
|
+
if (releasingInput === barrier)
|
|
711
|
+
releasingInput = null;
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
for (const entry of waitingInput) {
|
|
715
|
+
if (!sameControl(entry.control, control))
|
|
716
|
+
entry.settle(false);
|
|
717
|
+
else if (snapshot?.status === "live" && snapshot.fastLane)
|
|
718
|
+
entry.settle(true);
|
|
719
|
+
}
|
|
538
720
|
let next = null;
|
|
539
721
|
if (snapshot?.status === "live" &&
|
|
540
722
|
snapshot.daemonGeneration &&
|
|
@@ -570,7 +752,6 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
570
752
|
next.adapter !== inputAuthorityIdentity.adapter);
|
|
571
753
|
if (next === null || replaced) {
|
|
572
754
|
finishLinkSelection(false);
|
|
573
|
-
paneInput.invalidateSelection();
|
|
574
755
|
pendingWindowSwitch = null;
|
|
575
756
|
pendingWindowRename = null;
|
|
576
757
|
diagnosticWindowFrame = null;
|
|
@@ -715,6 +896,7 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
715
896
|
observed.expectedSemanticWindowId !== target.expectedSemanticWindowId)
|
|
716
897
|
return;
|
|
717
898
|
finishLinkSelection(false);
|
|
899
|
+
beginWindowSwitch(paneId, target.expectedSemanticWindowId, target);
|
|
718
900
|
let resolve;
|
|
719
901
|
const settled = new Promise((done) => {
|
|
720
902
|
resolve = done;
|
|
@@ -724,6 +906,7 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
724
906
|
paneId,
|
|
725
907
|
layout: snapshot,
|
|
726
908
|
received: false,
|
|
909
|
+
diagnostic: pendingWindowSwitch,
|
|
727
910
|
resolve,
|
|
728
911
|
timer: setTimeout(() => {
|
|
729
912
|
if (linkSelection === pending)
|
|
@@ -751,6 +934,7 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
751
934
|
}
|
|
752
935
|
const wrapper = (await expected.client.dispatch({
|
|
753
936
|
kind: "semantic-intent",
|
|
937
|
+
...(pending.diagnostic ? { operationId: pending.diagnostic.traceId } : {}),
|
|
754
938
|
intent: {
|
|
755
939
|
verb: "workspace.window.link.select",
|
|
756
940
|
workspaceName: expected.workspaceName,
|
|
@@ -763,6 +947,7 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
763
947
|
if (wrapper.kind !== "semantic-intent" ||
|
|
764
948
|
!result.success ||
|
|
765
949
|
result.data.operationId !== wrapper.operationId ||
|
|
950
|
+
(pending.diagnostic && wrapper.operationId !== pending.diagnostic.traceId) ||
|
|
766
951
|
result.data.daemonInstanceId !== expected.daemonGeneration ||
|
|
767
952
|
result.data.workspaceName !== expected.workspaceName ||
|
|
768
953
|
result.data.target.linkId !== target.linkId ||
|
|
@@ -772,6 +957,18 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
772
957
|
finishLinkSelection(false);
|
|
773
958
|
return;
|
|
774
959
|
}
|
|
960
|
+
if (pending.diagnostic) {
|
|
961
|
+
const receiptAtMicros = diagnosticNowMicros();
|
|
962
|
+
if (pending.diagnostic.timing)
|
|
963
|
+
pending.diagnostic.timing.receiptAtMicros = receiptAtMicros;
|
|
964
|
+
diagnose("window-switch-receipt", {
|
|
965
|
+
...pending.diagnostic,
|
|
966
|
+
operationId: wrapper.operationId,
|
|
967
|
+
selected: true,
|
|
968
|
+
applied: true,
|
|
969
|
+
...(options.detailedWindowSwitchTiming ? { phaseAtMicros: receiptAtMicros } : {}),
|
|
970
|
+
});
|
|
971
|
+
}
|
|
775
972
|
pending.received = true;
|
|
776
973
|
reconcileLinkSelection(pending.layout);
|
|
777
974
|
await settled;
|
|
@@ -1022,54 +1219,7 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
1022
1219
|
const target = next?.semanticWindowId ?? next?.windowName;
|
|
1023
1220
|
if (!pane || !target)
|
|
1024
1221
|
return;
|
|
1025
|
-
|
|
1026
|
-
try {
|
|
1027
|
-
const active = options.generation();
|
|
1028
|
-
const identity = active?.adapter?.paneCanonicalIdentity(pane);
|
|
1029
|
-
const clientGeneration = active?.client?.getSnapshot().generation;
|
|
1030
|
-
const startedAtMicros = diagnosticNowMicros();
|
|
1031
|
-
if (active?.status === "live" &&
|
|
1032
|
-
active.daemonGeneration &&
|
|
1033
|
-
identity &&
|
|
1034
|
-
Number.isSafeInteger(clientGeneration) &&
|
|
1035
|
-
startedAtMicros !== null) {
|
|
1036
|
-
pendingWindowSwitch = {
|
|
1037
|
-
traceId: createTraceId(),
|
|
1038
|
-
target,
|
|
1039
|
-
paneId: pane,
|
|
1040
|
-
startedAtMicros,
|
|
1041
|
-
daemonGeneration: active.daemonGeneration,
|
|
1042
|
-
clientGeneration: clientGeneration,
|
|
1043
|
-
rendererEpoch: active.rendererEpoch,
|
|
1044
|
-
sourceEpoch: identity.sourceEpoch,
|
|
1045
|
-
generation: identity.generation,
|
|
1046
|
-
incarnation: identity.incarnation,
|
|
1047
|
-
revision: identity.revision,
|
|
1048
|
-
stateHash: identity.stateHash,
|
|
1049
|
-
cols: identity.cols,
|
|
1050
|
-
rows: identity.rows,
|
|
1051
|
-
layoutPublished: false,
|
|
1052
|
-
selectionApplied: false,
|
|
1053
|
-
presentationPublished: false,
|
|
1054
|
-
followUpRequested: false,
|
|
1055
|
-
...(options.detailedWindowSwitchTiming
|
|
1056
|
-
? {
|
|
1057
|
-
timing: {
|
|
1058
|
-
receiptAtMicros: null,
|
|
1059
|
-
layoutAtMicros: null,
|
|
1060
|
-
presentationAtMicros: null,
|
|
1061
|
-
},
|
|
1062
|
-
}
|
|
1063
|
-
: {}),
|
|
1064
|
-
};
|
|
1065
|
-
armDiagnosticWindowFrame({ kind: "window-switch", ...pendingWindowSwitch });
|
|
1066
|
-
diagnose("window-switch-start", { ...pendingWindowSwitch });
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
catch {
|
|
1070
|
-
pendingWindowSwitch = null;
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1222
|
+
beginWindowSwitch(pane, target);
|
|
1073
1223
|
paneInput.selectPane(pane, { presentOptimistically: false });
|
|
1074
1224
|
},
|
|
1075
1225
|
async zoomPane(targetPane) {
|
|
@@ -1265,6 +1415,7 @@ export function createApplicationTerminalInteractionController(options) {
|
|
|
1265
1415
|
? {}
|
|
1266
1416
|
: { phaseAtMicros: pending.timing.presentationAtMicros }),
|
|
1267
1417
|
});
|
|
1418
|
+
maybeRequestWindowSwitchFrame();
|
|
1268
1419
|
},
|
|
1269
1420
|
observeDiagnosticWindowFrame() {
|
|
1270
1421
|
if (!options.diagnosticsEnabled)
|