[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

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
import { useEffect } from "react";
import { netcattyBridge } from "../../infrastructure/services/netcattyBridge";
import {
scheduleWindowInputFocus,
type ScheduledWindowInputFocus,
} from "./windowInputFocus";
export type MainWindowInputFocusRecoveryOptions = {
/** Close transient overlays before the window hides (#1722). */
onPageHidden?: () => void;
};
type Listener = () => void;
type MainWindowInputFocusRecoveryDocument = {
visibilityState: DocumentVisibilityState;
addEventListener: (eventName: "visibilitychange", listener: Listener) => void;
removeEventListener: (eventName: "visibilitychange", listener: Listener) => void;
};
type MainWindowInputFocusRecoveryWindow = {
addEventListener: (eventName: "focus", listener: Listener) => void;
removeEventListener: (eventName: "focus", listener: Listener) => void;
};
type MainWindowInputFocusRecoveryBridge = {
onWindowShown?: (callback: Listener) => Listener;
onWindowFocusRequested?: (callback: Listener) => Listener;
onWindowWillHide?: (callback: Listener) => Listener;
};
export type MainWindowInputFocusRecoveryDependencies = {
documentRef: MainWindowInputFocusRecoveryDocument;
windowRef?: MainWindowInputFocusRecoveryWindow;
bridge?: MainWindowInputFocusRecoveryBridge;
scheduleFocus?: () => ScheduledWindowInputFocus;
};
export function startMainWindowInputFocusRecovery(
options: MainWindowInputFocusRecoveryOptions = {},
dependencies: MainWindowInputFocusRecoveryDependencies = {
documentRef: document,
windowRef: window,
bridge: netcattyBridge.get(),
scheduleFocus: scheduleWindowInputFocus,
},
): () => void {
const { onPageHidden } = options;
const {
documentRef,
bridge,
scheduleFocus = scheduleWindowInputFocus,
} = dependencies;
let pendingFocusRecovery: ScheduledWindowInputFocus | null = null;
let pendingExplicitFocusRecovery = false;
const cancelPendingFocusRecovery = () => {
pendingFocusRecovery?.cancel();
pendingFocusRecovery = null;
};
const recoverFocus = (): boolean => {
if (documentRef.visibilityState !== "visible") return false;
pendingExplicitFocusRecovery = false;
cancelPendingFocusRecovery();
pendingFocusRecovery = scheduleFocus();
return true;
};
const dismissTransientUi = () => {
pendingExplicitFocusRecovery = false;
cancelPendingFocusRecovery();
onPageHidden?.();
};
const onVisibilityChange = () => {
if (documentRef.visibilityState === "hidden") {
dismissTransientUi();
return;
}
if (pendingExplicitFocusRecovery) {
recoverFocus();
}
};
documentRef.addEventListener("visibilitychange", onVisibilityChange);
const unsubscribeFocusRequested = bridge?.onWindowFocusRequested?.(() => {
pendingExplicitFocusRecovery = true;
recoverFocus();
});
const unsubscribeWillHide = bridge?.onWindowWillHide?.(() => {
dismissTransientUi();
});
return () => {
pendingExplicitFocusRecovery = false;
cancelPendingFocusRecovery();
documentRef.removeEventListener("visibilitychange", onVisibilityChange);
unsubscribeFocusRequested?.();
unsubscribeWillHide?.();
};
}
/**
* Recover OS/renderer input focus when the main process explicitly foregrounds the
* main window (#760, #1714, #1722).
*/
export function useMainWindowInputFocusRecovery(
options: MainWindowInputFocusRecoveryOptions = {},
): void {
const { onPageHidden } = options;
useEffect(() => {
return startMainWindowInputFocusRecovery({ onPageHidden });
}, [onPageHidden]);
}