[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:
89
components/terminal/focusTerminalSession.ts
Normal file
89
components/terminal/focusTerminalSession.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
type QueryRoot = {
|
||||
querySelector: (selector: string) => unknown | null;
|
||||
};
|
||||
|
||||
type QueryTarget = QueryRoot & {
|
||||
querySelector: (selector: string) => QueryTarget | FocusableTarget | null;
|
||||
};
|
||||
|
||||
type FocusableTarget = {
|
||||
focus?: () => void;
|
||||
};
|
||||
|
||||
/** Skip terminal refocus while a Radix dialog is open so deferred tmux actions do not steal modal focus. */
|
||||
export const hasOpenAppDialog = (
|
||||
doc: QueryRoot | null = typeof document !== "undefined" ? document : null,
|
||||
): boolean => {
|
||||
if (!doc) return false;
|
||||
return doc.querySelector('[role="dialog"][data-state="open"]') !== null;
|
||||
};
|
||||
|
||||
interface FocusTerminalSessionInputOptions {
|
||||
document?: QueryTarget | null;
|
||||
requestAnimationFrame?: (callback: () => void) => unknown;
|
||||
setTimeout?: (callback: () => void, delay: number) => unknown;
|
||||
retryDelays?: readonly number[];
|
||||
}
|
||||
|
||||
const escapeAttributeValue = (value: string): string =>
|
||||
value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
||||
|
||||
export const TERMINAL_SESSION_RESTORE_FOCUS_EVENT = "netcatty:terminal-session-restore-focus";
|
||||
|
||||
export type TerminalSessionRestoreFocusDetail = {
|
||||
sessionId: string;
|
||||
};
|
||||
|
||||
const dispatchTerminalSessionRestoreFocus = (sessionId: string): void => {
|
||||
if (typeof window === "undefined") return;
|
||||
window.dispatchEvent(new CustomEvent<TerminalSessionRestoreFocusDetail>(
|
||||
TERMINAL_SESSION_RESTORE_FOCUS_EVENT,
|
||||
{ detail: { sessionId } },
|
||||
));
|
||||
};
|
||||
|
||||
export const focusTerminalSessionInput = (
|
||||
sessionId: string | null | undefined,
|
||||
options: FocusTerminalSessionInputOptions = {},
|
||||
): void => {
|
||||
if (!sessionId) return;
|
||||
|
||||
const doc = options.document ?? (typeof document !== "undefined" ? document : null);
|
||||
if (!doc) return;
|
||||
|
||||
const raf = options.requestAnimationFrame
|
||||
?? (typeof requestAnimationFrame !== "undefined"
|
||||
? requestAnimationFrame
|
||||
: (callback: () => void) => {
|
||||
callback();
|
||||
return undefined;
|
||||
});
|
||||
const scheduleTimeout = options.setTimeout
|
||||
?? (typeof setTimeout !== "undefined"
|
||||
? setTimeout
|
||||
: (callback: () => void) => {
|
||||
callback();
|
||||
return undefined;
|
||||
});
|
||||
const retryDelays = options.retryDelays ?? [50];
|
||||
const paneSelector = `[data-session-id="${escapeAttributeValue(sessionId)}"]`;
|
||||
|
||||
const focusTarget = () => {
|
||||
if (hasOpenAppDialog(doc)) {
|
||||
dispatchTerminalSessionRestoreFocus(sessionId);
|
||||
return;
|
||||
}
|
||||
|
||||
const pane = doc.querySelector(paneSelector) as QueryTarget | null;
|
||||
const textarea = pane?.querySelector("textarea.xterm-helper-textarea") as FocusableTarget | null;
|
||||
textarea?.focus?.();
|
||||
dispatchTerminalSessionRestoreFocus(sessionId);
|
||||
};
|
||||
|
||||
raf(() => {
|
||||
focusTarget();
|
||||
retryDelays.forEach((delay) => {
|
||||
scheduleTimeout(focusTarget, delay);
|
||||
});
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user