fix terminal popup release behavior (#1403)
This commit is contained in:
@@ -128,6 +128,10 @@ function captureError(source, err, extra) {
|
||||
}
|
||||
}
|
||||
|
||||
function captureDiagnostic(source, message, extra) {
|
||||
captureError(source, new Error(String(message || "diagnostic")), extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete log files older than LOG_RETENTION_DAYS.
|
||||
*/
|
||||
@@ -317,10 +321,21 @@ function registerHandlers(ipcMain) {
|
||||
ipcMain.handle("netcatty:crashLogs:read", async (_event, { fileName }) => readLog(fileName));
|
||||
ipcMain.handle("netcatty:crashLogs:clear", async () => clearLogs());
|
||||
ipcMain.handle("netcatty:crashLogs:openDir", async () => openDir());
|
||||
ipcMain.handle("netcatty:diagnostics:log", async (_event, payload) => {
|
||||
const source = typeof payload?.source === "string" && payload.source.trim()
|
||||
? payload.source.trim()
|
||||
: "renderer-diagnostic";
|
||||
const message = typeof payload?.message === "string" && payload.message.trim()
|
||||
? payload.message.trim()
|
||||
: "diagnostic";
|
||||
captureDiagnostic(source, message, payload?.extra);
|
||||
return { success: true };
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init,
|
||||
captureError,
|
||||
captureDiagnostic,
|
||||
registerHandlers,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
const crashLogBridge = require("../crashLogBridge.cjs");
|
||||
|
||||
function createTerminalPopupWindowApi(ctx) {
|
||||
with (ctx) {
|
||||
const terminalPopupWindows = new Map();
|
||||
@@ -29,6 +31,14 @@ function createTerminalPopupWindowApi(ctx) {
|
||||
? payload.title.trim()
|
||||
: "Terminal";
|
||||
|
||||
crashLogBridge.captureDiagnostic("terminal-popup", "creating popup window", {
|
||||
popupId: payload?.popupId,
|
||||
title,
|
||||
isDev,
|
||||
popupX,
|
||||
popupY,
|
||||
});
|
||||
|
||||
const win = new BrowserWindow({
|
||||
title,
|
||||
width: popupWidth,
|
||||
@@ -39,9 +49,8 @@ function createTerminalPopupWindowApi(ctx) {
|
||||
backgroundColor,
|
||||
icon: appIcon,
|
||||
show: false,
|
||||
frame: isMac,
|
||||
titleBarStyle: isMac ? "hiddenInset" : undefined,
|
||||
trafficLightPosition: isMac ? { x: 12, y: 12 } : undefined,
|
||||
frame: false,
|
||||
...(isMac ? { trafficLightPosition: { x: 12, y: 12 } } : {}),
|
||||
webPreferences: {
|
||||
preload,
|
||||
contextIsolation: true,
|
||||
@@ -53,6 +62,11 @@ function createTerminalPopupWindowApi(ctx) {
|
||||
|
||||
const popupId = String(payload?.popupId || Date.now());
|
||||
terminalPopupWindows.set(popupId, win);
|
||||
crashLogBridge.captureDiagnostic("terminal-popup", "popup BrowserWindow created", {
|
||||
popupId,
|
||||
title,
|
||||
webContentsId: win.webContents?.id,
|
||||
});
|
||||
|
||||
try {
|
||||
win.webContents?.setWindowOpenHandler?.(
|
||||
@@ -66,6 +80,30 @@ function createTerminalPopupWindowApi(ctx) {
|
||||
terminalPopupWindows.delete(popupId);
|
||||
});
|
||||
|
||||
try {
|
||||
win.webContents?.on?.("did-fail-load", (_event, errorCode, errorDescription, validatedURL) => {
|
||||
console.warn("[TerminalPopup] Failed to load renderer", {
|
||||
popupId,
|
||||
errorCode,
|
||||
errorDescription,
|
||||
validatedURL,
|
||||
});
|
||||
});
|
||||
win.webContents?.on?.("render-process-gone", (_event, details) => {
|
||||
console.warn("[TerminalPopup] Renderer process gone", { popupId, details });
|
||||
});
|
||||
win.webContents?.on?.("console-message", (_event, level, message, line, sourceId) => {
|
||||
crashLogBridge.captureDiagnostic("terminal-popup-console", message, {
|
||||
popupId,
|
||||
level,
|
||||
line,
|
||||
sourceId,
|
||||
});
|
||||
});
|
||||
} catch {
|
||||
// ignore diagnostics wiring failures
|
||||
}
|
||||
|
||||
win.on("page-title-updated", (e) => { e.preventDefault(); });
|
||||
|
||||
try {
|
||||
@@ -76,34 +114,56 @@ function createTerminalPopupWindowApi(ctx) {
|
||||
|
||||
applyWindowOpacityToWindow(win);
|
||||
|
||||
const popupPath = "/#/terminal-popup";
|
||||
if (isMac) {
|
||||
try {
|
||||
win.setWindowButtonVisibility(true);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
win.setWindowButtonPosition({ x: 12, y: 12 });
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
const popupPath = "#/terminal-popup";
|
||||
|
||||
if (isDev) {
|
||||
try {
|
||||
const baseUrl = getDevRendererBaseUrl(devServerUrl);
|
||||
crashLogBridge.captureDiagnostic("terminal-popup", "loading dev popup URL", {
|
||||
popupId,
|
||||
url: `${baseUrl}${popupPath}`,
|
||||
});
|
||||
await win.loadURL(`${baseUrl}${popupPath}`);
|
||||
} catch (e) {
|
||||
console.warn("[TerminalPopup] Dev server not reachable", e);
|
||||
crashLogBridge.captureError("terminal-popup", e, {
|
||||
popupId,
|
||||
step: "load dev popup URL",
|
||||
});
|
||||
await win.loadURL(`app://netcatty/index.html${popupPath}`);
|
||||
}
|
||||
} else {
|
||||
crashLogBridge.captureDiagnostic("terminal-popup", "loading packaged popup URL", {
|
||||
popupId,
|
||||
url: `app://netcatty/index.html${popupPath}`,
|
||||
});
|
||||
await win.loadURL(`app://netcatty/index.html${popupPath}`);
|
||||
}
|
||||
|
||||
const delivery = await sendWhenRendererReady(
|
||||
win,
|
||||
"netcatty:window:terminalPopupConfig",
|
||||
{ ...payload, popupId },
|
||||
{ timeoutMs: 10000 },
|
||||
);
|
||||
|
||||
if (!delivery.success) {
|
||||
try { win.destroy(); } catch { /* ignore */ }
|
||||
terminalPopupWindows.delete(popupId);
|
||||
return { success: false, error: delivery.error || "Popup failed to receive config" };
|
||||
}
|
||||
|
||||
win.webContents.send("netcatty:window:terminalPopupConfig", { ...payload, popupId });
|
||||
crashLogBridge.captureDiagnostic("terminal-popup", "popup config delivered", {
|
||||
popupId,
|
||||
title,
|
||||
});
|
||||
showAndFocusWindow(win);
|
||||
crashLogBridge.captureDiagnostic("terminal-popup", "popup window shown", {
|
||||
popupId,
|
||||
title,
|
||||
visible: typeof win.isVisible === "function" ? win.isVisible() : undefined,
|
||||
});
|
||||
return { success: true, popupId };
|
||||
}
|
||||
|
||||
|
||||
@@ -360,8 +360,16 @@ function createBridgeRegistrar(context) {
|
||||
if (!payload || typeof payload !== "object") {
|
||||
return { success: false, error: "Invalid popup payload" };
|
||||
}
|
||||
crashLogBridge.captureDiagnostic("terminal-popup", "openTerminalPopup IPC received", {
|
||||
title: payload.title,
|
||||
parentSessionId: payload.parentSessionId,
|
||||
startupCommand: payload.startupCommand,
|
||||
sourceSessionId: payload.sourceSession?.id,
|
||||
sourceProtocol: payload.sourceSession?.protocol,
|
||||
sourceHostLabel: payload.sourceSession?.hostLabel,
|
||||
});
|
||||
const sourceWindow = BrowserWindow.fromWebContents(event.sender);
|
||||
return await getWindowManager().openTerminalPopupWindow(electronModule, {
|
||||
const result = await getWindowManager().openTerminalPopupWindow(electronModule, {
|
||||
preload,
|
||||
devServerUrl: effectiveDevServerUrl,
|
||||
isDev,
|
||||
@@ -370,7 +378,18 @@ function createBridgeRegistrar(context) {
|
||||
electronDir,
|
||||
sourceWindow,
|
||||
}, payload);
|
||||
crashLogBridge.captureDiagnostic("terminal-popup", "openTerminalPopup IPC result", {
|
||||
title: payload.title,
|
||||
success: result?.success,
|
||||
error: result?.error,
|
||||
popupId: result?.popupId,
|
||||
});
|
||||
return result;
|
||||
} catch (err) {
|
||||
crashLogBridge.captureError("terminal-popup", err, {
|
||||
title: payload?.title,
|
||||
parentSessionId: payload?.parentSessionId,
|
||||
});
|
||||
console.error("[Main] Failed to open terminal popup:", err);
|
||||
return { success: false, error: err?.message || "Failed to open terminal popup" };
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ const updateAvailableListeners = new Set();
|
||||
const updateNotAvailableListeners = new Set();
|
||||
const updateErrorListeners = new Set();
|
||||
const updateNeedsSaveListeners = new Set();
|
||||
const terminalPopupConfigState = {
|
||||
pending: null,
|
||||
listeners: new Set(),
|
||||
};
|
||||
|
||||
function cleanupTransferListeners(transferId) {
|
||||
transferProgressListeners.delete(transferId);
|
||||
@@ -129,6 +133,20 @@ ipcRenderer.on("netcatty:zmodem:detect", (_event, payload) => {
|
||||
if (!set) return;
|
||||
set.forEach((cb) => { try { cb({ type: "detect", ...payload }); } catch {} });
|
||||
});
|
||||
|
||||
ipcRenderer.on("netcatty:window:terminalPopupConfig", (_event, payload) => {
|
||||
if (terminalPopupConfigState.listeners.size === 0) {
|
||||
terminalPopupConfigState.pending = payload;
|
||||
return;
|
||||
}
|
||||
terminalPopupConfigState.listeners.forEach((cb) => {
|
||||
try {
|
||||
cb(payload);
|
||||
} catch (err) {
|
||||
console.error("Terminal popup config callback failed", err);
|
||||
}
|
||||
});
|
||||
});
|
||||
ipcRenderer.on("netcatty:zmodem:progress", (_event, payload) => {
|
||||
const set = zmodemListeners.get(payload.sessionId);
|
||||
if (!set) return;
|
||||
@@ -656,6 +674,7 @@ const api = createPreloadApi({
|
||||
updateNotAvailableListeners,
|
||||
updateErrorListeners,
|
||||
updateNeedsSaveListeners,
|
||||
terminalPopupConfigState,
|
||||
uploadProgressListeners,
|
||||
uploadCompleteListeners,
|
||||
uploadErrorListeners,
|
||||
|
||||
@@ -128,10 +128,23 @@ function createPreloadApi(ctx) {
|
||||
openTerminalPopup: async (payload) => {
|
||||
return ipcRenderer.invoke("netcatty:window:openTerminalPopup", payload);
|
||||
},
|
||||
logDiagnostic: async (payload) => {
|
||||
return ipcRenderer.invoke("netcatty:diagnostics:log", payload);
|
||||
},
|
||||
onTerminalPopupConfig: (cb) => {
|
||||
const handler = (_event, payload) => cb(payload);
|
||||
ipcRenderer.on("netcatty:window:terminalPopupConfig", handler);
|
||||
return () => ipcRenderer.removeListener("netcatty:window:terminalPopupConfig", handler);
|
||||
terminalPopupConfigState.listeners.add(cb);
|
||||
if (terminalPopupConfigState.pending) {
|
||||
const pending = terminalPopupConfigState.pending;
|
||||
terminalPopupConfigState.pending = null;
|
||||
queueMicrotask(() => {
|
||||
try {
|
||||
cb(pending);
|
||||
} catch (err) {
|
||||
console.error("Terminal popup config callback failed", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
return () => terminalPopupConfigState.listeners.delete(cb);
|
||||
},
|
||||
readRemoteHistory: async (sessionId, limit) => {
|
||||
return ipcRenderer.invoke("netcatty:ssh:readRemoteHistory", { sessionId, limit });
|
||||
|
||||
Reference in New Issue
Block a user