mirror of
https://github.com/wavetermdev/waveterm
synced 2026-05-24 09:18:27 +00:00
`mousedown` activity signaling was structured such that async telemetry
concerns leaked into event handling. This change moves fire-and-forget
behavior to the model boundary and keeps telemetry failures non-fatal.
- **`mousedown` handler path**
- `AppKeyHandlers` now calls `GlobalModel.getInstance().setIsActive()`
directly (no async wrapper in the handler).
- **`GlobalModel.setIsActive` structure**
- `setIsActive()` is now synchronous (`void`).
- Throttle logic remains unchanged.
- Electron telemetry call is executed via `util.fireAndForget(...)`
inside `setIsActive()`.
- **Telemetry error containment**
- `getApi().setIsActive()` is wrapped in `try/catch` inside the
fire-and-forget callback.
- Errors are logged with `console.log("setIsActive error", e)` and do
not bubble.
- **Focused coverage**
- Added `frontend/app/store/global-model.test.ts` for:
- fire-and-forget invocation + throttling behavior
- error logging/swallowing on rejected telemetry call
```ts
setIsActive(): void {
const now = Date.now();
if (now - this.lastSetIsActiveTs < GlobalModel.IsActiveThrottleMs) {
return;
}
this.lastSetIsActiveTs = now;
util.fireAndForget(async () => {
try {
await getApi().setIsActive();
} catch (e) {
console.log("setIsActive error", e);
}
});
}
```
<!-- START COPILOT CODING AGENT TIPS -->
---
🔒 GitHub Advanced Security automatically protects Copilot coding agent
pull requests. You can protect all pull requests by enabling Advanced
Security for your repositories. [Learn more about Advanced
Security.](https://gh.io/cca-advanced-security)
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
Co-authored-by: sawka <mike@commandline.dev>
87 lines
5.6 KiB
TypeScript
87 lines
5.6 KiB
TypeScript
// Copyright 2025, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import { contextBridge, ipcRenderer, Rectangle, WebviewTag } from "electron";
|
|
|
|
// update type in custom.d.ts (ElectronApi type)
|
|
contextBridge.exposeInMainWorld("api", {
|
|
getAuthKey: () => ipcRenderer.sendSync("get-auth-key"),
|
|
getIsDev: () => ipcRenderer.sendSync("get-is-dev"),
|
|
getPlatform: () => ipcRenderer.sendSync("get-platform"),
|
|
getCursorPoint: () => ipcRenderer.sendSync("get-cursor-point"),
|
|
getUserName: () => ipcRenderer.sendSync("get-user-name"),
|
|
getHostName: () => ipcRenderer.sendSync("get-host-name"),
|
|
getDataDir: () => ipcRenderer.sendSync("get-data-dir"),
|
|
getConfigDir: () => ipcRenderer.sendSync("get-config-dir"),
|
|
getHomeDir: () => ipcRenderer.sendSync("get-home-dir"),
|
|
getAboutModalDetails: () => ipcRenderer.sendSync("get-about-modal-details"),
|
|
getWebviewPreload: () => ipcRenderer.sendSync("get-webview-preload"),
|
|
getZoomFactor: () => ipcRenderer.sendSync("get-zoom-factor"),
|
|
openNewWindow: () => ipcRenderer.send("open-new-window"),
|
|
showWorkspaceAppMenu: (workspaceId) => ipcRenderer.send("workspace-appmenu-show", workspaceId),
|
|
showBuilderAppMenu: (builderId) => ipcRenderer.send("builder-appmenu-show", builderId),
|
|
showContextMenu: (workspaceId, menu) => ipcRenderer.send("contextmenu-show", workspaceId, menu),
|
|
onContextMenuClick: (callback: (id: string | null) => void) =>
|
|
ipcRenderer.on("contextmenu-click", (_event, id: string | null) => callback(id)),
|
|
downloadFile: (filePath) => ipcRenderer.send("download", { filePath }),
|
|
openExternal: (url) => {
|
|
if (url && typeof url === "string") {
|
|
ipcRenderer.send("open-external", url);
|
|
} else {
|
|
console.error("Invalid URL passed to openExternal:", url);
|
|
}
|
|
},
|
|
getEnv: (varName) => ipcRenderer.sendSync("get-env", varName),
|
|
onFullScreenChange: (callback) =>
|
|
ipcRenderer.on("fullscreen-change", (_event, isFullScreen) => callback(isFullScreen)),
|
|
onZoomFactorChange: (callback) =>
|
|
ipcRenderer.on("zoom-factor-change", (_event, zoomFactor) => callback(zoomFactor)),
|
|
onUpdaterStatusChange: (callback) => ipcRenderer.on("app-update-status", (_event, status) => callback(status)),
|
|
getUpdaterStatus: () => ipcRenderer.sendSync("get-app-update-status"),
|
|
getUpdaterChannel: () => ipcRenderer.sendSync("get-updater-channel"),
|
|
installAppUpdate: () => ipcRenderer.send("install-app-update"),
|
|
onMenuItemAbout: (callback) => ipcRenderer.on("menu-item-about", callback),
|
|
updateWindowControlsOverlay: (rect) => ipcRenderer.send("update-window-controls-overlay", rect),
|
|
onReinjectKey: (callback) => ipcRenderer.on("reinject-key", (_event, waveEvent) => callback(waveEvent)),
|
|
setWebviewFocus: (focused: number) => ipcRenderer.send("webview-focus", focused),
|
|
registerGlobalWebviewKeys: (keys) => ipcRenderer.send("register-global-webview-keys", keys),
|
|
onControlShiftStateUpdate: (callback) =>
|
|
ipcRenderer.on("control-shift-state-update", (_event, state) => callback(state)),
|
|
createWorkspace: () => ipcRenderer.send("create-workspace"),
|
|
switchWorkspace: (workspaceId) => ipcRenderer.send("switch-workspace", workspaceId),
|
|
deleteWorkspace: (workspaceId) => ipcRenderer.send("delete-workspace", workspaceId),
|
|
setActiveTab: (tabId) => ipcRenderer.send("set-active-tab", tabId),
|
|
createTab: () => ipcRenderer.send("create-tab"),
|
|
closeTab: (workspaceId, tabId, confirmClose) => ipcRenderer.invoke("close-tab", workspaceId, tabId, confirmClose),
|
|
setWindowInitStatus: (status) => ipcRenderer.send("set-window-init-status", status),
|
|
onWaveInit: (callback) => ipcRenderer.on("wave-init", (_event, initOpts) => callback(initOpts)),
|
|
onBuilderInit: (callback) => ipcRenderer.on("builder-init", (_event, initOpts) => callback(initOpts)),
|
|
sendLog: (log) => ipcRenderer.send("fe-log", log),
|
|
onQuicklook: (filePath: string) => ipcRenderer.send("quicklook", filePath),
|
|
openNativePath: (filePath: string) => ipcRenderer.send("open-native-path", filePath),
|
|
captureScreenshot: (rect: Rectangle) => ipcRenderer.invoke("capture-screenshot", rect),
|
|
setKeyboardChordMode: () => ipcRenderer.send("set-keyboard-chord-mode"),
|
|
clearWebviewStorage: (webContentsId: number) => ipcRenderer.invoke("clear-webview-storage", webContentsId),
|
|
setWaveAIOpen: (isOpen: boolean) => ipcRenderer.send("set-waveai-open", isOpen),
|
|
closeBuilderWindow: () => ipcRenderer.send("close-builder-window"),
|
|
incrementTermCommands: (opts?: { isRemote?: boolean; isWsl?: boolean; isDurable?: boolean }) =>
|
|
ipcRenderer.send("increment-term-commands", opts),
|
|
nativePaste: () => ipcRenderer.send("native-paste"),
|
|
openBuilder: (appId?: string) => ipcRenderer.send("open-builder", appId),
|
|
setBuilderWindowAppId: (appId: string) => ipcRenderer.send("set-builder-window-appid", appId),
|
|
doRefresh: () => ipcRenderer.send("do-refresh"),
|
|
saveTextFile: (fileName: string, content: string) => ipcRenderer.invoke("save-text-file", fileName, content),
|
|
setIsActive: () => ipcRenderer.invoke("set-is-active"),
|
|
});
|
|
|
|
// Custom event for "new-window"
|
|
ipcRenderer.on("webview-new-window", (e, webContentsId, details) => {
|
|
const event = new CustomEvent("new-window", { detail: details });
|
|
document.getElementById("webview").dispatchEvent(event);
|
|
});
|
|
|
|
ipcRenderer.on("webcontentsid-from-blockid", (e, blockId, responseCh) => {
|
|
const webviewElem: WebviewTag = document.querySelector("div[data-blockid='" + blockId + "'] webview");
|
|
const wcId = webviewElem?.dataset?.webcontentsid;
|
|
ipcRenderer.send(responseCh, wcId);
|
|
});
|