[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
94
components/terminal/connectionLogBuffer.ts
Normal file
94
components/terminal/connectionLogBuffer.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* A bounded, append-only text buffer that retains only the last `maxChars`
|
||||
* characters — the connection log used for diagnostics/replay.
|
||||
*
|
||||
* The naive implementation (`log += chunk; if (log.length > max) log =
|
||||
* log.slice(-max)`) flattens a ~max-length string on *every* append once the
|
||||
* cap is reached — on the render thread, for every output chunk including each
|
||||
* echoed keystroke.
|
||||
*
|
||||
* Instead, data is coalesced into a small, bounded number of fixed-size blocks
|
||||
* (~`maxChars / blockSize`, e.g. ~16 for the 1 MB cap). New data accumulates in
|
||||
* an open `tail`; once it reaches `blockSize` it is sealed into a block. Trimming
|
||||
* the oldest data therefore only ever drops/slices a handful of blocks — never
|
||||
* one array element per append, which would make trim O(number of appends) and
|
||||
* defeat the purpose. Append is amortized O(chunk); the full string is
|
||||
* materialized only on `toString()` (called rarely, on finalize).
|
||||
*/
|
||||
export interface ConnectionLogBuffer {
|
||||
append(chunk: string): void;
|
||||
toString(): string;
|
||||
reset(): void;
|
||||
/**
|
||||
* Number of internal string segments currently retained. Exposed for tests
|
||||
* to assert the bounded-memory / bounded-trim property.
|
||||
*/
|
||||
segmentCount(): number;
|
||||
}
|
||||
|
||||
const DEFAULT_BLOCK_SIZE = 64 * 1024;
|
||||
|
||||
export function createConnectionLogBuffer(
|
||||
maxChars: number,
|
||||
blockSize: number = DEFAULT_BLOCK_SIZE,
|
||||
): ConnectionLogBuffer {
|
||||
let blocks: string[] = []; // sealed blocks, oldest first, each up to ~blockSize
|
||||
let tail = ""; // open block currently being filled (newest data)
|
||||
let total = 0; // total retained length across blocks + tail
|
||||
|
||||
const trim = () => {
|
||||
let overflow = total - maxChars;
|
||||
if (overflow <= 0) return;
|
||||
// Drop/slice whole blocks from the front. `blocks.length` is bounded by
|
||||
// ~maxChars/blockSize, so this shift is O(small constant), not O(appends).
|
||||
while (overflow > 0 && blocks.length > 0) {
|
||||
const head = blocks[0];
|
||||
if (head.length <= overflow) {
|
||||
blocks.shift();
|
||||
total -= head.length;
|
||||
overflow -= head.length;
|
||||
} else {
|
||||
blocks[0] = head.slice(overflow);
|
||||
total -= overflow;
|
||||
overflow = 0;
|
||||
}
|
||||
}
|
||||
// Only reachable when the tail alone exceeds the cap (e.g. blockSize >=
|
||||
// maxChars); keep its last `maxChars` characters.
|
||||
if (overflow > 0) {
|
||||
tail = tail.slice(overflow);
|
||||
total -= overflow;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
append(chunk: string): void {
|
||||
if (!chunk) return;
|
||||
// A single chunk at/over the cap can only contribute its own tail.
|
||||
if (chunk.length >= maxChars) {
|
||||
blocks = [];
|
||||
tail = chunk.slice(chunk.length - maxChars);
|
||||
total = tail.length;
|
||||
return;
|
||||
}
|
||||
tail += chunk;
|
||||
total += chunk.length;
|
||||
if (tail.length >= blockSize) {
|
||||
blocks.push(tail);
|
||||
tail = "";
|
||||
}
|
||||
if (total > maxChars) trim();
|
||||
},
|
||||
toString(): string {
|
||||
return blocks.length > 0 ? blocks.join("") + tail : tail;
|
||||
},
|
||||
reset(): void {
|
||||
blocks = [];
|
||||
tail = "";
|
||||
total = 0;
|
||||
},
|
||||
segmentCount(): number {
|
||||
return blocks.length + (tail.length > 0 ? 1 : 0);
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user