[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:
172
domain/terminalHibernate.ts
Normal file
172
domain/terminalHibernate.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import type { TerminalSession, TerminalSettings } from "./models/terminal";
|
||||
|
||||
/** Compile-time kill switch for terminal hibernate (Settings > Terminal still controls user default). */
|
||||
export const TERMINAL_HIBERNATE_ENABLED = true;
|
||||
|
||||
/** Default delay before hibernating a hidden tab (seconds). */
|
||||
export const TERMINAL_HIBERNATE_DELAY_SEC_DEFAULT = 5;
|
||||
|
||||
export const TERMINAL_HIBERNATE_DELAY_SEC_MIN = 5;
|
||||
|
||||
export const TERMINAL_HIBERNATE_DELAY_SEC_MAX = 600;
|
||||
|
||||
/** Default delay in milliseconds (legacy constant for tests). */
|
||||
export const TERMINAL_HIBERNATE_DELAY_MS = TERMINAL_HIBERNATE_DELAY_SEC_DEFAULT * 1000;
|
||||
|
||||
export function normalizeHibernateHiddenTabsDelaySec(value: unknown): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) {
|
||||
return TERMINAL_HIBERNATE_DELAY_SEC_DEFAULT;
|
||||
}
|
||||
return Math.min(
|
||||
TERMINAL_HIBERNATE_DELAY_SEC_MAX,
|
||||
Math.max(TERMINAL_HIBERNATE_DELAY_SEC_MIN, Math.round(value)),
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveTerminalHibernateDelayMs(
|
||||
settings?: Pick<TerminalSettings, "hibernateHiddenTabsDelaySec"> | null,
|
||||
): number {
|
||||
return normalizeHibernateHiddenTabsDelaySec(settings?.hibernateHiddenTabsDelaySec) * 1000;
|
||||
}
|
||||
|
||||
export function resolveTerminalHibernateEnabled(
|
||||
settings?: Pick<TerminalSettings, "hibernateHiddenTabs"> | null,
|
||||
): boolean {
|
||||
if (!TERMINAL_HIBERNATE_ENABLED) return false;
|
||||
return settings?.hibernateHiddenTabs === true;
|
||||
}
|
||||
|
||||
export function resolveTerminalHibernateEnabledForProtocol(
|
||||
settings: Pick<TerminalSettings, "hibernateHiddenTabs"> | null | undefined,
|
||||
protocol: string | null | undefined,
|
||||
): boolean {
|
||||
return protocol !== "local" && resolveTerminalHibernateEnabled(settings);
|
||||
}
|
||||
|
||||
/** Hidden connected and ended remote terminals can both release their xterm runtime. */
|
||||
export function canHibernateTerminalRuntimeStatus(
|
||||
status: TerminalSession["status"],
|
||||
): boolean {
|
||||
return status === "connected" || status === "disconnected";
|
||||
}
|
||||
|
||||
export function canHibernateTerminalRuntimeSession(
|
||||
status: TerminalSession["status"],
|
||||
backendSessionId: string | null | undefined,
|
||||
): boolean {
|
||||
if (!canHibernateTerminalRuntimeStatus(status)) return false;
|
||||
return status === "disconnected" || Boolean(backendSessionId);
|
||||
}
|
||||
|
||||
export function shouldKeepTerminalBackgroundWorkActive(
|
||||
settings: Pick<TerminalSettings, "hibernateHiddenTabs"> | null | undefined,
|
||||
protocol: string | null | undefined,
|
||||
isVisible: boolean,
|
||||
): boolean {
|
||||
return isVisible || !resolveTerminalHibernateEnabledForProtocol(settings, protocol);
|
||||
}
|
||||
|
||||
/** Block hibernate while a file transfer or drag-drop session is in progress. */
|
||||
export function isTerminalFileTransferActive(options: {
|
||||
zmodemActive: boolean;
|
||||
ymodemInProgress: boolean;
|
||||
isDraggingOver: boolean;
|
||||
}): boolean {
|
||||
return options.zmodemActive || options.ymodemInProgress || options.isDraggingOver;
|
||||
}
|
||||
|
||||
/** Lines kept when snapshotting xterm scrollback before hibernate. */
|
||||
export const TERMINAL_HIBERNATE_SNAPSHOT_MAX_LINES = 3_000;
|
||||
|
||||
/** Default chunk size for replaying hibernate snapshots without blocking the main thread. */
|
||||
export const TERMINAL_HIBERNATE_REPLAY_CHUNK_BYTES_DEFAULT = 16 * 1024;
|
||||
|
||||
export const TERMINAL_HIBERNATE_REPLAY_CHUNK_BYTES_MIN = 4 * 1024;
|
||||
|
||||
export const TERMINAL_HIBERNATE_REPLAY_CHUNK_BYTES_MAX = 64 * 1024;
|
||||
|
||||
/** Hidden tabs whose xterm runtime is kept alive (WebGL suspended) before full hibernate. */
|
||||
export const TERMINAL_HIBERNATE_KEEP_RENDERER_COUNT_DEFAULT = 2;
|
||||
|
||||
export const TERMINAL_HIBERNATE_KEEP_RENDERER_COUNT_MIN = 0;
|
||||
|
||||
export const TERMINAL_HIBERNATE_KEEP_RENDERER_COUNT_MAX = 12;
|
||||
|
||||
/** Max chars buffered in renderer while xterm is hibernated (post-snapshot output). */
|
||||
export const TERMINAL_HIBERNATE_BUFFER_MAX_CHARS = 512 * 1024;
|
||||
|
||||
export function capHibernateBuffer(buffer: string, maxChars = TERMINAL_HIBERNATE_BUFFER_MAX_CHARS): string {
|
||||
if (buffer.length <= maxChars) return buffer;
|
||||
return buffer.slice(buffer.length - maxChars);
|
||||
}
|
||||
|
||||
export function capHibernateBufferByLines(text: string, maxLines: number): string {
|
||||
if (maxLines <= 0 || text.length === 0) return text;
|
||||
const lines = text.split("\n");
|
||||
if (lines.length <= maxLines) return text;
|
||||
return lines.slice(lines.length - maxLines).join("\n");
|
||||
}
|
||||
|
||||
export function normalizeHibernateReplayChunkBytes(value: unknown): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) {
|
||||
return TERMINAL_HIBERNATE_REPLAY_CHUNK_BYTES_DEFAULT;
|
||||
}
|
||||
return Math.min(
|
||||
TERMINAL_HIBERNATE_REPLAY_CHUNK_BYTES_MAX,
|
||||
Math.max(TERMINAL_HIBERNATE_REPLAY_CHUNK_BYTES_MIN, Math.round(value)),
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeHibernateKeepRendererCount(value: unknown): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) {
|
||||
return TERMINAL_HIBERNATE_KEEP_RENDERER_COUNT_DEFAULT;
|
||||
}
|
||||
return Math.min(
|
||||
TERMINAL_HIBERNATE_KEEP_RENDERER_COUNT_MAX,
|
||||
Math.max(TERMINAL_HIBERNATE_KEEP_RENDERER_COUNT_MIN, Math.round(value)),
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveTerminalHibernateReplayChunkBytes(
|
||||
settings?: Pick<TerminalSettings, "hibernateReplayChunkBytes"> | null,
|
||||
): number {
|
||||
return normalizeHibernateReplayChunkBytes(settings?.hibernateReplayChunkBytes);
|
||||
}
|
||||
|
||||
export function resolveHibernateKeepRendererCount(
|
||||
settings?: Pick<TerminalSettings, "hibernateKeepRendererCount"> | null,
|
||||
): number {
|
||||
return normalizeHibernateKeepRendererCount(settings?.hibernateKeepRendererCount);
|
||||
}
|
||||
|
||||
export function resolveHibernateSkipAltScreen(
|
||||
settings?: Pick<TerminalSettings, "hibernateSkipAltScreen"> | null,
|
||||
): boolean {
|
||||
return settings?.hibernateSkipAltScreen !== false;
|
||||
}
|
||||
|
||||
export function resolveHibernatePreferWasmSerialize(
|
||||
settings?: Pick<TerminalSettings, "hibernatePreferWasmSerialize"> | null,
|
||||
): boolean {
|
||||
return settings?.hibernatePreferWasmSerialize === true;
|
||||
}
|
||||
|
||||
export function shouldSkipHibernateForAltScreen(
|
||||
alternateScreen: boolean,
|
||||
settings?: Pick<TerminalSettings, "hibernateSkipAltScreen"> | null,
|
||||
): boolean {
|
||||
return alternateScreen && resolveHibernateSkipAltScreen(settings);
|
||||
}
|
||||
|
||||
/** Snapshot + buffered output to replay when recreating xterm after hibernate. */
|
||||
export type TerminalHibernateWakePayload = {
|
||||
/** Full serialized terminal state -- preferred wake history (coherent ANSI). */
|
||||
snapshot: string;
|
||||
/** Visible viewport rows; used with scrollback only when snapshot is empty. */
|
||||
viewportSnapshot?: string;
|
||||
/** Older scrollback rows; used with viewport only when snapshot is empty. */
|
||||
scrollbackSnapshot?: string;
|
||||
pendingBuffer: string;
|
||||
/** True when the pane was hibernated while a full-screen app owned the alt buffer. */
|
||||
alternateScreen: boolean;
|
||||
};
|
||||
Reference in New Issue
Block a user