From b89940213165106fe98f16ac2168005523acb730 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:53:18 +0000 Subject: [PATCH 1/2] Initial plan From f0a297adb632eff06b50c295a0ced80ccc16f44c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:56:43 +0000 Subject: [PATCH 2/2] fix: scope devtools shortcuts to app window input events Co-authored-by: OpenSource03 <29690431+OpenSource03@users.noreply.github.com> Agent-Logs-Url: https://github.com/OpenSource03/harnss/sessions/0e68305c-7295-4057-b260-63ecce60384d --- .../lib/__tests__/devtools-shortcuts.test.ts | 30 +++++++++++++++++++ electron/src/lib/devtools-shortcuts.ts | 27 +++++++++++++++++ electron/src/main.ts | 20 ++++++------- 3 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 electron/src/lib/__tests__/devtools-shortcuts.test.ts create mode 100644 electron/src/lib/devtools-shortcuts.ts diff --git a/electron/src/lib/__tests__/devtools-shortcuts.test.ts b/electron/src/lib/__tests__/devtools-shortcuts.test.ts new file mode 100644 index 0000000..6ccc8dc --- /dev/null +++ b/electron/src/lib/__tests__/devtools-shortcuts.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; +import { isDevToolsShortcut } from "../devtools-shortcuts"; + +describe("isDevToolsShortcut", () => { + it("matches F12 on keyDown", () => { + expect(isDevToolsShortcut({ type: "keyDown", key: "F12" }, "darwin")).toBe(true); + expect(isDevToolsShortcut({ type: "keyUp", key: "F12" }, "darwin")).toBe(false); + }); + + it("matches Cmd+Alt+I on macOS only when meta is pressed", () => { + expect(isDevToolsShortcut({ type: "keyDown", key: "i", meta: true, alt: true }, "darwin")).toBe(true); + expect(isDevToolsShortcut({ type: "keyDown", key: "i", control: true, alt: true }, "darwin")).toBe(false); + }); + + it("matches Ctrl+Alt+I on non-macOS", () => { + expect(isDevToolsShortcut({ type: "keyDown", key: "I", control: true, alt: true }, "linux")).toBe(true); + expect(isDevToolsShortcut({ type: "keyDown", key: "I", meta: true, alt: true }, "linux")).toBe(false); + }); + + it("matches Cmd/Ctrl+Shift+J", () => { + expect(isDevToolsShortcut({ type: "keyDown", key: "j", meta: true, shift: true }, "darwin")).toBe(true); + expect(isDevToolsShortcut({ type: "keyDown", key: "j", control: true, shift: true }, "win32")).toBe(true); + }); + + it("does not match unrelated combos", () => { + expect(isDevToolsShortcut({ type: "keyDown", key: "i", meta: true }, "darwin")).toBe(false); + expect(isDevToolsShortcut({ type: "keyDown", key: "j", meta: true }, "darwin")).toBe(false); + expect(isDevToolsShortcut({ type: "keyDown", key: "k", meta: true, shift: true }, "darwin")).toBe(false); + }); +}); diff --git a/electron/src/lib/devtools-shortcuts.ts b/electron/src/lib/devtools-shortcuts.ts new file mode 100644 index 0000000..02d3a07 --- /dev/null +++ b/electron/src/lib/devtools-shortcuts.ts @@ -0,0 +1,27 @@ +type ShortcutInput = { + type?: string; + key?: string; + control?: boolean; + alt?: boolean; + shift?: boolean; + meta?: boolean; +}; + +/** + * Matches the app's DevTools shortcuts from keyboard input events. + * Scoped to window input handling so it never captures OS-global shortcuts. + */ +export function isDevToolsShortcut(input: ShortcutInput, platform: NodeJS.Platform): boolean { + if (input.type !== "keyDown") return false; + + const key = (input.key ?? "").toLowerCase(); + if (key === "f12") return true; + + const commandOrControl = platform === "darwin" ? !!input.meta : !!input.control; + if (!commandOrControl) return false; + + if (input.alt && key === "i") return true; + if (input.shift && key === "j") return true; + + return false; +} diff --git a/electron/src/main.ts b/electron/src/main.ts index afaf703..d401391 100644 --- a/electron/src/main.ts +++ b/electron/src/main.ts @@ -1,5 +1,5 @@ import { execSync } from "child_process"; -import { app, BrowserWindow, clipboard, globalShortcut, ipcMain, Menu, session, shell, systemPreferences } from "electron"; +import { app, BrowserWindow, clipboard, ipcMain, Menu, session, shell, systemPreferences } from "electron"; import path from "path"; import http from "http"; import contextMenu from "electron-context-menu"; @@ -25,6 +25,7 @@ import { migrateFromOpenAcpUi } from "./lib/migration"; import { glassEnabled, liquidGlass } from "./lib/glass"; import { initAutoUpdater, getIsInstallingUpdate } from "./lib/updater"; import { initPostHog, shutdownPostHog, reinitPostHog, captureEvent } from "./lib/posthog"; +import { isDevToolsShortcut } from "./lib/devtools-shortcuts"; import { sessions } from "./ipc/claude-sessions"; import { acpSessions, getAcpAnalyticsPropertiesForSession } from "./ipc/acp-sessions"; import { terminals } from "./ipc/terminal"; @@ -149,6 +150,13 @@ function createWindow(): void { } }); } + + mainWindow.webContents.on("before-input-event", (event, input) => { + if (!isDevToolsShortcut(input, process.platform)) return; + event.preventDefault(); + log("DEVTOOLS", `Shortcut ${input.key} triggered`); + openDevToolsWindow(); + }); } // Renderer uses this to decide whether the transparency toggle is available. @@ -345,19 +353,9 @@ app.whenReady().then(() => { app.dock.setIcon(path.join(__dirname, "../../build/icon.png")); } - const shortcuts = ["CommandOrControl+Alt+I", "F12", "CommandOrControl+Shift+J"]; - for (const shortcut of shortcuts) { - const ok = globalShortcut.register(shortcut, () => { - log("DEVTOOLS", `Shortcut ${shortcut} triggered`); - openDevToolsWindow(); - }); - log("DEVTOOLS", `Register ${shortcut}: ${ok ? "OK" : "FAILED"}`); - } }); app.on("will-quit", (event) => { - globalShortcut.unregisterAll(); - // When an update is being installed, let the updater control the quit lifecycle. // In that case, fire-and-forget PostHog shutdown and do not delay quit. if (getIsInstallingUpdate()) {