tmux-ide 2.9.4 → 2.9.5
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/bin/cli.js +4354 -3376
- package/package.json +7 -6
- package/packages/contracts/src/session-runtime.ts +2 -0
- package/packages/contracts/src/terminal-delivery.ts +4 -0
- package/packages/core/src/terminal-compact-capability.ts +31 -1
- package/packages/core/src/terminal-delivery.ts +234 -32
- package/packages/core/src/terminal-replica-hash-cache.ts +162 -21
- package/packages/core/src/terminal-replica-owned-row.ts +165 -10
- package/packages/core/src/terminal-replica.ts +43 -21
- package/packages/daemon/dist/lib/daemon-embed.js +19 -34
- package/packages/daemon/dist/lib/runtime-trace-writer.js +133 -0
- package/packages/daemon/dist/lib/tmux-client-execution.js +37 -2
- package/packages/daemon/dist/lib/tmux-server-owner.js +20 -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/native/tmux/darwin-arm64/manifest.json +5 -1
- package/packages/daemon/dist/native/tmux/darwin-arm64/tmux +0 -0
- package/packages/daemon/dist/native/tmux/darwin-x64/manifest.json +5 -1
- package/packages/daemon/dist/native/tmux/darwin-x64/tmux +0 -0
- package/packages/daemon/dist/native/tmux/linux-arm64/manifest.json +5 -1
- package/packages/daemon/dist/native/tmux/linux-arm64/tmux +0 -0
- package/packages/daemon/dist/native/tmux/linux-x64/licenses/libevent_core-2.1.so.7.txt +0 -0
- package/packages/daemon/dist/native/tmux/linux-x64/manifest.json +5 -1
- package/packages/daemon/dist/native/tmux/linux-x64/tmux +0 -0
- package/packages/daemon/dist/terminal/attachments/native-runtime.js +6 -6
- package/packages/daemon/dist/terminal/mirror/control-channel.js +379 -75
- package/packages/daemon/dist/terminal/mirror/mirror-service.js +43 -3
- package/packages/daemon/dist/terminal/mirror/native-grid-projection.js +20 -11
- package/packages/daemon/dist/terminal/mirror/session-channel.js +639 -811
- package/packages/daemon/dist/terminal/mirror/stock-pane-snapshot.js +177 -0
- package/packages/daemon/dist/terminal/session-runtime/causal-cell-ledger.js +1 -1
- package/packages/daemon/dist/terminal/session-runtime/registry.js +145 -15
- package/packages/daemon/dist/terminal/session-runtime/stock-capture-fidelity.js +12 -0
- package/packages/daemon/dist/terminal/session-runtime/terminal-delivery-hub.js +128 -10
- package/packages/daemon/dist/terminal/session-runtime/terminal-replica-interpreter.js +21 -4
- package/packages/daemon/dist/terminal/session-runtime/terminal-replica-owner.js +19 -2
- package/packages/daemon/dist/terminal/session-runtime/xterm-terminal-interpreter-backend.js +17 -19
- package/packages/daemon/dist/tui/mirror/open-tui-workspace-runtime-port.js +64 -4
- package/packages/daemon/dist/tui/mirror/pane-surface.jsx +10 -1
- package/packages/daemon/dist/tui/mirror/reference-performance-trace.js +117 -71
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-input-ingress.js +27 -3
- package/packages/daemon/dist/tui/mirror/runtime/pane-scoped-terminal-surface.jsx +17 -4
- package/packages/daemon/dist/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.js +19 -0
- package/packages/daemon/dist/tui/mirror/runtime/terminal-input-adapter.js +10 -2
- package/packages/daemon/dist/tui/mirror/testing/styled-frame-oracle.js +156 -0
- package/packages/daemon/src/lib/daemon-embed.ts +22 -34
- package/packages/daemon/src/lib/runtime-trace-writer.ts +132 -0
- package/packages/daemon/src/lib/tmux-client-execution.ts +47 -2
- package/packages/daemon/src/lib/tmux-server-owner.ts +19 -3
- package/packages/daemon/src/terminal/attachments/native-runtime.ts +6 -0
- package/packages/daemon/src/terminal/mirror/control-channel.ts +428 -84
- package/packages/daemon/src/terminal/mirror/events.ts +7 -7
- package/packages/daemon/src/terminal/mirror/mirror-service.ts +49 -3
- package/packages/daemon/src/terminal/mirror/native-grid-projection.ts +21 -11
- package/packages/daemon/src/terminal/mirror/pane-feed.ts +9 -4
- package/packages/daemon/src/terminal/mirror/session-channel.ts +705 -859
- package/packages/daemon/src/terminal/mirror/stock-pane-snapshot.ts +228 -0
- package/packages/daemon/src/terminal/session-runtime/causal-cell-ledger.ts +1 -1
- package/packages/daemon/src/terminal/session-runtime/registry.ts +203 -32
- package/packages/daemon/src/terminal/session-runtime/runtime-observability.ts +7 -0
- package/packages/daemon/src/terminal/session-runtime/stock-capture-fidelity.ts +13 -0
- package/packages/daemon/src/terminal/session-runtime/terminal-delivery-hub.ts +142 -8
- package/packages/daemon/src/terminal/session-runtime/terminal-replica-interpreter.ts +26 -3
- package/packages/daemon/src/terminal/session-runtime/terminal-replica-owner.ts +25 -1
- package/packages/daemon/src/terminal/session-runtime/xterm-terminal-interpreter-backend.ts +26 -20
- package/packages/daemon/src/tui/mirror/open-tui-workspace-runtime-port.ts +63 -4
- package/packages/daemon/src/tui/mirror/pane-surface.tsx +9 -1
- package/packages/daemon/src/tui/mirror/reference-performance-trace.ts +141 -90
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-input-ingress.ts +27 -4
- package/packages/daemon/src/tui/mirror/runtime/pane-scoped-terminal-surface.tsx +61 -31
- package/packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.ts +31 -3
- package/packages/daemon/src/tui/mirror/runtime/terminal-input-adapter.ts +9 -6
- package/packages/daemon/src/tui/mirror/testing/styled-frame-oracle.ts +209 -0
- package/packages/daemon-client/src/terminal-fast-lane.ts +53 -4
- package/packages/daemon-client/src/workspace-client-types.ts +1 -0
- package/packages/daemon-client/src/workspace-client.ts +78 -5
- package/packages/tmux-bridge/src/index.ts +1 -0
- package/packages/tmux-bridge/src/runner.ts +25 -1
|
@@ -7,15 +7,15 @@ import type { NativeGridCapture } from "./native-grid-capture.ts";
|
|
|
7
7
|
* addresses (`%N`, `@N`, `$N`) never cross this boundary. Pane content arrives
|
|
8
8
|
* as a typed event stream a renderer can apply verbatim to a VT emulator:
|
|
9
9
|
*
|
|
10
|
-
* reset → seed → [held deltas] →
|
|
10
|
+
* reset → seed → cursor → [held deltas] → live deltas…
|
|
11
11
|
*
|
|
12
12
|
* The seed batch is emitted ATOMICALLY (one synchronous callback sequence) so
|
|
13
13
|
* a consumer can apply it as a single paint: `reset` replaces emulator state
|
|
14
14
|
* (tmux is a painter, not a stream — never composite two captures), `seed`
|
|
15
|
-
* carries one capture from one instant,
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* carries one capture from one instant, and `cursor` installs that capture's
|
|
16
|
+
* cursor and mode metadata. Strictly post-capture output then replays once;
|
|
17
|
+
* installing older cursor/mode metadata after those bytes would overwrite their
|
|
18
|
+
* effects. Everything after the held tail is a live delta.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
/** Modes observed at the native capture seam; omitted fields stay unknown. */
|
|
@@ -72,8 +72,8 @@ export type MirrorPaneEvent =
|
|
|
72
72
|
}
|
|
73
73
|
| {
|
|
74
74
|
/** Delivery state changed. `backpressure` mirrors tmux `%pause`;
|
|
75
|
-
* `requested` is a subscriber's own freeze. A `resumed` flow event
|
|
76
|
-
*
|
|
75
|
+
* `requested` is a subscriber's own freeze. A `resumed` flow event follows
|
|
76
|
+
* the fresh atomic seed batch and its held output. */
|
|
77
77
|
type: "flow";
|
|
78
78
|
state: "paused" | "resumed";
|
|
79
79
|
reason: "backpressure" | "requested";
|
|
@@ -73,6 +73,10 @@ export interface MirrorServiceOptions {
|
|
|
73
73
|
controlModeOwnershipRegistry?: ControlModeOwnershipRegistry;
|
|
74
74
|
/** Emitted only after an event-triggered list-clients proof of a native client. */
|
|
75
75
|
onNativeClientActivity?: (session: string) => void;
|
|
76
|
+
captureInputWrite?: (
|
|
77
|
+
session: string,
|
|
78
|
+
...args: Parameters<NonNullable<SessionChannelOptions["captureInputWrite"]>>
|
|
79
|
+
) => ReturnType<NonNullable<SessionChannelOptions["captureInputWrite"]>>;
|
|
76
80
|
onInputWrite?: (
|
|
77
81
|
session: string,
|
|
78
82
|
action: InputAction,
|
|
@@ -496,6 +500,47 @@ export class MirrorService {
|
|
|
496
500
|
return this.channels.get(session)?.channel.flowSnapshot() ?? null;
|
|
497
501
|
}
|
|
498
502
|
|
|
503
|
+
// Cumulative scalar totals sampled after each retired channel finishes disposal.
|
|
504
|
+
// Keep evidence of residual listeners without retaining retired channel objects.
|
|
505
|
+
private readonly retiredListenerCounts = {
|
|
506
|
+
channels: 0,
|
|
507
|
+
failures: 0,
|
|
508
|
+
pane: 0,
|
|
509
|
+
layout: 0,
|
|
510
|
+
layoutAuthority: 0,
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
qualificationListeners() {
|
|
514
|
+
const active = { pane: 0, layout: 0, layoutAuthority: 0 };
|
|
515
|
+
for (const entry of this.channels.values()) {
|
|
516
|
+
const counts = entry.channel.qualificationListeners();
|
|
517
|
+
active.pane += counts.pane;
|
|
518
|
+
active.layout += counts.layout;
|
|
519
|
+
active.layoutAuthority += counts.layoutAuthority;
|
|
520
|
+
}
|
|
521
|
+
return Object.freeze({
|
|
522
|
+
active: Object.freeze(active),
|
|
523
|
+
retired: Object.freeze({ ...this.retiredListenerCounts }),
|
|
524
|
+
pendingDisposals: this.pendingDisposals.size,
|
|
525
|
+
sessionExit: this.sessionExitListeners.size,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
private async disposeObservedChannel(channel: SessionChannel): Promise<void> {
|
|
530
|
+
try {
|
|
531
|
+
await channel.dispose();
|
|
532
|
+
} catch (error) {
|
|
533
|
+
this.retiredListenerCounts.failures += 1;
|
|
534
|
+
throw error;
|
|
535
|
+
} finally {
|
|
536
|
+
const counts = channel.qualificationListeners();
|
|
537
|
+
this.retiredListenerCounts.channels += 1;
|
|
538
|
+
this.retiredListenerCounts.pane += counts.pane;
|
|
539
|
+
this.retiredListenerCounts.layout += counts.layout;
|
|
540
|
+
this.retiredListenerCounts.layoutAuthority += counts.layoutAuthority;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
499
544
|
activeChannelCount(): number {
|
|
500
545
|
return this.channels.size;
|
|
501
546
|
}
|
|
@@ -512,7 +557,7 @@ export class MirrorService {
|
|
|
512
557
|
this.updateWindowMembership(entry.channel, []);
|
|
513
558
|
this.channelSessions.delete(entry.channel);
|
|
514
559
|
try {
|
|
515
|
-
await entry.channel
|
|
560
|
+
await this.disposeObservedChannel(entry.channel);
|
|
516
561
|
} finally {
|
|
517
562
|
entry.releaseAuthority();
|
|
518
563
|
}
|
|
@@ -625,6 +670,8 @@ export class MirrorService {
|
|
|
625
670
|
}
|
|
626
671
|
},
|
|
627
672
|
onNativeClientActivity: () => this.opts.onNativeClientActivity?.(session),
|
|
673
|
+
captureInputWrite: (action, semanticPaneId) =>
|
|
674
|
+
this.opts.captureInputWrite?.(session, action, semanticPaneId),
|
|
628
675
|
onInputWrite: (action, startedAtMicros, endedAtMicros, pendingBeforeSend) =>
|
|
629
676
|
this.opts.onInputWrite?.(
|
|
630
677
|
session,
|
|
@@ -686,8 +733,7 @@ export class MirrorService {
|
|
|
686
733
|
this.updateWindowMembership(entry.channel, []);
|
|
687
734
|
this.channelSessions.delete(entry.channel);
|
|
688
735
|
if (this.channels.get(session) === entry) this.channels.delete(session);
|
|
689
|
-
const disposal = entry.channel
|
|
690
|
-
.dispose()
|
|
736
|
+
const disposal = this.disposeObservedChannel(entry.channel)
|
|
691
737
|
.catch(() => {})
|
|
692
738
|
.finally(() => entry.releaseAuthority());
|
|
693
739
|
this.drainingChannels.set(session, disposal);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TERMINAL_REPLICA_EMPTY_CELL, TERMINAL_REPLICA_SPACE_CELL } from "@tmux-ide/core";
|
|
1
2
|
import type {
|
|
2
3
|
TerminalReplicaCell,
|
|
3
4
|
TerminalReplicaColor,
|
|
@@ -23,13 +24,7 @@ export function nativeProjectedRowSource(row: TerminalReplicaRow) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const defaultColor: TerminalReplicaColor = Object.freeze({ kind: "default" });
|
|
26
|
-
const empty
|
|
27
|
-
grapheme: "",
|
|
28
|
-
width: 1,
|
|
29
|
-
foreground: defaultColor,
|
|
30
|
-
background: defaultColor,
|
|
31
|
-
attributes: 0,
|
|
32
|
-
});
|
|
27
|
+
const empty = TERMINAL_REPLICA_EMPTY_CELL;
|
|
33
28
|
const acsKeys = [..."+,-.0`abcdefghijklmnopqrstuvwxyz{|}~"];
|
|
34
29
|
const acsValues = [..."→←↑↓▮◆▒␉␌␍␊°±␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£·"];
|
|
35
30
|
const acs = new Map(acsKeys.map((key, index) => [key, acsValues[index]!]));
|
|
@@ -45,7 +40,7 @@ function color(value: number): TerminalReplicaColor {
|
|
|
45
40
|
|
|
46
41
|
function paint(cell: NativeGridCaptureCell): TerminalReplicaCell {
|
|
47
42
|
const bits = cell.attributes;
|
|
48
|
-
return
|
|
43
|
+
return {
|
|
49
44
|
grapheme: bits & 0x80 ? (acs.get(cell.text) ?? cell.text) : cell.text,
|
|
50
45
|
width: cell.width === 2 ? 2 : 1,
|
|
51
46
|
foreground: color(cell.foreground),
|
|
@@ -58,7 +53,22 @@ function paint(cell: NativeGridCaptureCell): TerminalReplicaCell {
|
|
|
58
53
|
(bits & 0x10 ? 32 : 0) |
|
|
59
54
|
(bits & 0x20 ? 64 : 0) |
|
|
60
55
|
(bits & 0x100 ? 128 : 0),
|
|
61
|
-
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Only normalized, internally constructed paint can reuse trusted core values.
|
|
60
|
+
// Foreign native fields have already been read in the original paint order.
|
|
61
|
+
function freezePaint(cell: TerminalReplicaCell): TerminalReplicaCell {
|
|
62
|
+
if (
|
|
63
|
+
cell.width === 1 &&
|
|
64
|
+
cell.attributes === 0 &&
|
|
65
|
+
cell.foreground === defaultColor &&
|
|
66
|
+
cell.background === defaultColor
|
|
67
|
+
) {
|
|
68
|
+
if (cell.grapheme === "") return TERMINAL_REPLICA_EMPTY_CELL;
|
|
69
|
+
if (cell.grapheme === " ") return TERMINAL_REPLICA_SPACE_CELL;
|
|
70
|
+
}
|
|
71
|
+
return Object.freeze(cell);
|
|
62
72
|
}
|
|
63
73
|
|
|
64
74
|
/**
|
|
@@ -105,12 +115,12 @@ export function projectNativeGridRow(
|
|
|
105
115
|
x + native.width > columns
|
|
106
116
|
) {
|
|
107
117
|
const span = native.flags & 0x80 ? Math.max(1, Math.min(native.width, columns - x)) : 1;
|
|
108
|
-
const blank =
|
|
118
|
+
const blank = freezePaint({ ...styled, grapheme: "", width: 1 as const });
|
|
109
119
|
for (let offset = 0; offset < span; offset++) cells[x + offset] = blank;
|
|
110
120
|
x += span - 1;
|
|
111
121
|
continue;
|
|
112
122
|
}
|
|
113
|
-
cells[x] = styled;
|
|
123
|
+
cells[x] = freezePaint(styled);
|
|
114
124
|
if (native.width === 2) {
|
|
115
125
|
cells[++x] = Object.freeze({ ...styled, grapheme: "", width: 0 });
|
|
116
126
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import type { NativeGridCapture } from "./native-grid-capture.ts";
|
|
2
2
|
/**
|
|
3
3
|
* PaneFeed — PURE per-subscriber delivery gate implementing the atomic
|
|
4
|
-
* seed/reseed recipe
|
|
4
|
+
* seed/reseed recipe. This gate assumes the caller supplies a valid capture
|
|
5
|
+
* seam; FIFO command replies alone do not prove that precondition.
|
|
5
6
|
*
|
|
6
7
|
* tmux is a painter, not a stream: history and the live screen are separate
|
|
7
8
|
* authorities, and a capture may only be spliced onto a live stream at an
|
|
8
|
-
* explicit seam.
|
|
9
|
-
* `capture-pane` reply comes back at the exact point in the byte stream where
|
|
10
|
-
* the capture was taken, so:
|
|
9
|
+
* explicit seam. For a VALID seam the caller must establish:
|
|
11
10
|
*
|
|
12
11
|
* - every `%output` delta read BEFORE the capture reply was produced before
|
|
13
12
|
* the capture instant → its bytes are already IN the capture → DISCARD
|
|
@@ -15,6 +14,12 @@ import type { NativeGridCapture } from "./native-grid-capture.ts";
|
|
|
15
14
|
* - every delta read AFTER the capture reply is strictly-after-capture →
|
|
16
15
|
* it must be applied on top of the seed → no gap.
|
|
17
16
|
*
|
|
17
|
+
* Known limitation: a sibling pane backlog can hold a capture reply while
|
|
18
|
+
* post-capture target output drains ahead of it. Ordinary capture reply order
|
|
19
|
+
* therefore does not establish this seam; that path can discard newer output.
|
|
20
|
+
* See docs/guides/tmux-boundary-model.md and TM02 for the real-wire reproducer.
|
|
21
|
+
* Native atomic snapshot/offset commit has a separate, stronger contract.
|
|
22
|
+
*
|
|
18
23
|
* The cursor probe is a second command issued back-to-back with the capture.
|
|
19
24
|
* Deltas that land between the two replies (normally none — both commands sit
|
|
20
25
|
* in one command-queue batch) are HELD and replayed after the seed but BEFORE
|