diff --git a/packages/debugger-shell/package.json b/packages/debugger-shell/package.json index c81ab54ea16a..f0adb329b2cf 100644 --- a/packages/debugger-shell/package.json +++ b/packages/debugger-shell/package.json @@ -46,7 +46,7 @@ "fb-dotslash": "0.5.8" }, "devDependencies": { - "electron": "39.0.0", + "electron": "43.0.0-alpha.3", "semver": "^7.1.3" }, "files": [ diff --git a/packages/debugger-shell/src/electron/AppMenu.js b/packages/debugger-shell/src/electron/AppMenu.js new file mode 100644 index 000000000000..6ef24bcadf38 --- /dev/null +++ b/packages/debugger-shell/src/electron/AppMenu.js @@ -0,0 +1,162 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +const {BrowserWindow, Menu, app, nativeImage, shell} = + // $FlowFixMe[unclear-type] We have no Flow types for the Electron API. + require('electron') as any; + +const {isMacOSAtLeast} = require('./utils'); + +export function configureAppMenu(): void { + const template = [ + ...(process.platform === 'darwin' ? [{role: 'appMenu'}] : []), + { + label: 'File', + submenu: [ + { + label: 'Reload App', + ...menuSymbol('arrow.clockwise'), + accelerator: + process.platform === 'darwin' ? 'Command+R' : 'Control+R', + click: () => invokeCommand('inspector-main.reload'), + }, + { + label: 'Reload DevTools', + ...menuSymbol('none'), + accelerator: process.platform === 'darwin' ? 'Option+R' : 'Alt+R', + click: () => BrowserWindow.getFocusedWindow()?.webContents.reload(), + }, + {type: 'separator'}, + { + label: 'Quick Open…', + ...menuSymbol('doc.text.magnifyingglass'), + accelerator: + process.platform === 'darwin' ? 'Command+P' : 'Control+P', + click: () => invokeCommand('quick-open.show'), + }, + {type: 'separator'}, + {role: 'close'}, + ], + }, + { + label: 'Edit', + submenu: [ + {role: 'undo'}, + {role: 'redo'}, + {type: 'separator'}, + {role: 'cut'}, + {role: 'copy'}, + {role: 'paste'}, + {role: 'selectAll'}, + ], + }, + { + label: 'View', + submenu: [ + { + label: 'Command Palette…', + ...menuSymbol('filemenu.and.selection'), + accelerator: + process.platform === 'darwin' + ? 'Command+Shift+P' + : 'Control+Shift+P', + click: () => invokeCommand('quick-open.show-command-menu'), + }, + {type: 'separator'}, + // Enable Developer Tools only in development + ...(!app.isPackaged + ? [{type: 'separator'}, {role: 'toggleDevTools'}] + : []), + {type: 'separator'}, + {role: 'resetZoom'}, + {role: 'zoomIn'}, + {role: 'zoomOut'}, + {type: 'separator'}, + {role: 'togglefullscreen'}, + ], + }, + {role: 'windowMenu'}, + { + role: 'help', + submenu: [ + { + label: 'Keyboard Shortcuts', + ...menuSymbol('keyboard'), + click: () => invokeCommand('settings.shortcuts'), + }, + {type: 'separator'}, + { + label: 'React Native Website', + ...menuSymbol('text.rectangle.page'), + click: () => shell.openExternal('https://reactnative.dev'), + }, + { + label: 'Release Notes', + ...menuSymbol('none'), + click: () => + shell.openExternal( + 'https://github.com/facebook/react-native/releases', + ), + }, + ], + }, + ]; + const menu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(menu); +} + +function menuSymbol(symbolName: string): {icon?: unknown} { + if (!isMacOSAtLeast(26)) { + return {}; + } + return { + icon: + symbolName === 'none' + ? createMenuSpacer() + : nativeImage.createMenuSymbol(symbolName), + }; +} + +function createMenuSpacer() { + const size = 16; + const buf = Buffer.alloc(size * size * 4); + + for (let i = 0; i < buf.length; i += 4) { + buf[i] = 0; // R + buf[i + 1] = 0; // G + buf[i + 2] = 0; // B + buf[i + 3] = 1; // A + } + + const spacer = nativeImage.createFromBitmap(buf, { + width: size, + height: size, + }); + + // On macOS, mark as template so it lives in the same gutter as other + // template icons and adapts to light/dark menu bars. + if (process.platform === 'darwin') { + spacer.setTemplateImage(true); + } + + return spacer; +} + +function invokeCommand(commandId: string): void { + const win = BrowserWindow.getFocusedWindow(); + win.webContents.executeJavaScript( + `(async () => { + const UI = await import('./ui/legacy/legacy.js'); + return UI.ActionRegistry.ActionRegistry.instance() + .getAction(${JSON.stringify(commandId)})?.execute(); + })()`, + true, + ); +} diff --git a/packages/debugger-shell/src/electron/MainInstanceEntryPoint.js b/packages/debugger-shell/src/electron/MainInstanceEntryPoint.js index 77ae7250645a..851590f45793 100644 --- a/packages/debugger-shell/src/electron/MainInstanceEntryPoint.js +++ b/packages/debugger-shell/src/electron/MainInstanceEntryPoint.js @@ -8,13 +8,14 @@ * @format */ +import {configureAppMenu} from './AppMenu.js'; import SettingsStore from './SettingsStore.js'; const path = require('path'); const util = require('util'); // $FlowFixMe[unclear-type] We have no Flow types for the Electron API. -const {BrowserWindow, Menu, app, shell, ipcMain} = require('electron') as any; +const {BrowserWindow, app, shell, ipcMain} = require('electron') as any; const appSettings = new SettingsStore(); const windowMetadata = new WeakMap< @@ -102,34 +103,6 @@ function handleLaunchArgs(argv: string[]) { frontendWindow.focus(); } -function configureAppMenu() { - const template = [ - ...(process.platform === 'darwin' ? [{role: 'appMenu'}] : []), - {role: 'fileMenu'}, - {role: 'editMenu'}, - {role: 'viewMenu'}, - {role: 'windowMenu'}, - { - role: 'help', - submenu: [ - { - label: 'React Native Website', - click: () => shell.openExternal('https://reactnative.dev'), - }, - { - label: 'Release Notes', - click: () => - shell.openExternal( - 'https://github.com/facebook/react-native/releases', - ), - }, - ], - }, - ]; - const menu = Menu.buildFromTemplate(template); - Menu.setApplicationMenu(menu); -} - function getSavedWindowPosition( windowKey: string, ): ?{width: number, height: number, x?: number, y?: number} { diff --git a/packages/debugger-shell/src/electron/utils.js b/packages/debugger-shell/src/electron/utils.js new file mode 100644 index 000000000000..bf45bf0032e7 --- /dev/null +++ b/packages/debugger-shell/src/electron/utils.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +/** Equivalent of Swift's `if #available(macOS 26, *)`. */ +export function isMacOSAtLeast(major: number): boolean { + return ( + process.platform === 'darwin' && + // $FlowFixMe[prop-missing] + Number.parseInt(process.getSystemVersion().split('.')[0], 10) >= major + ); +} diff --git a/yarn.lock b/yarn.lock index 444bb5166a78..a004fbdfd392 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1094,10 +1094,10 @@ glob "^7.1.6" minimatch "^3.0.4" -"@electron/get@^2.0.0": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.3.tgz#fba552683d387aebd9f3fcadbcafc8e12ee4f960" - integrity sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ== +"@electron/get@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@electron/get/-/get-3.1.0.tgz#22c5a0bd917ab201badeb77bc4ad18cba54cb4ec" + integrity sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ== dependencies: debug "^4.1.1" env-paths "^2.2.0" @@ -1109,20 +1109,19 @@ optionalDependencies: global-agent "^3.0.0" -"@electron/get@^3.0.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-3.1.0.tgz#22c5a0bd917ab201badeb77bc4ad18cba54cb4ec" - integrity sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ== +"@electron/get@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@electron/get/-/get-5.0.0.tgz#3c7ec0e26480ce51a487d54c8a10233460531021" + integrity sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA== dependencies: debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^11.8.5" + env-paths "^3.0.0" + graceful-fs "^4.2.11" progress "^2.0.3" - semver "^6.2.0" + semver "^7.6.3" sumchecker "^3.0.1" optionalDependencies: - global-agent "^3.0.0" + undici "^7.24.4" "@electron/notarize@^2.1.0": version "2.5.0" @@ -2252,13 +2251,20 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== -"@types/node@*", "@types/node@^22.7.7": +"@types/node@*": version "22.15.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.18.tgz#2f8240f7e932f571c2d45f555ba0b6c3f7a75963" integrity sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg== dependencies: undici-types "~6.21.0" +"@types/node@^24.9.0": + version "24.12.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.4.tgz#2709745569811dcbdc57c097fafdd387c6330382" + integrity sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA== + dependencies: + undici-types "~7.16.0" + "@types/react@^19.1.0": version "19.1.8" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.8.tgz#ff8395f2afb764597265ced15f8dddb0720ae1c3" @@ -3949,13 +3955,13 @@ electron-to-chromium@^1.5.73: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.113.tgz#1175b8ba4170541e44e9afa8b992e5bbfff0d150" integrity sha512-wjT2O4hX+wdWPJ76gWSkMhcHAV2PTMX+QetUCPYEdCIe+cxmgzzSSiGRCKW8nuh4mwKZlpv0xvoW7OF2X+wmHg== -electron@39.0.0: - version "39.0.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-39.0.0.tgz#6906720c5bd3c40f98f6e01802fdc301654c4550" - integrity sha512-UejnuOK4jpRZUq7MkEAnR/szsRWLKBJAdvn6j3xdQLT57fVv13VSNdaUHHjSheaqGzNhCGIdkPsPJnGJVh5kiA== +electron@43.0.0-alpha.3: + version "43.0.0-alpha.3" + resolved "https://registry.yarnpkg.com/electron/-/electron-43.0.0-alpha.3.tgz#467d87092f201b0e45336e8c0a7a5bdfe573da9c" + integrity sha512-O1q0JpdHnQ8WCSfZl2krebLC/bLoGn9Y0A3FA1dcNiol8XIVfZ3XtmoavUItITye38a4tTQdgzqgcVORMCchRQ== dependencies: - "@electron/get" "^2.0.0" - "@types/node" "^22.7.7" + "@electron/get" "^5.0.0" + "@types/node" "^24.9.0" extract-zip "^2.0.1" emittery@^0.13.1: @@ -4005,6 +4011,11 @@ env-paths@^2.2.0, env-paths@^2.2.1: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +env-paths@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-3.0.0.tgz#2f1e89c2f6dbd3408e1b1711dd82d62e317f58da" + integrity sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A== + envinfo@^7.13.0: version "7.13.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31" @@ -5136,7 +5147,7 @@ got@^11.8.5: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -8447,7 +8458,7 @@ semver@^7.1.3, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semve resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== -semver@^7.7.3: +semver@^7.6.3, semver@^7.7.3: version "7.8.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.0.tgz#ed0661039fcbcda2ce71f01fa6adbefaa77040df" integrity sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA== @@ -9315,11 +9326,21 @@ undici-types@~6.21.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== + undici@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/undici/-/undici-6.23.0.tgz#7953087744d9095a96f115de3140ca3828aff3a4" integrity sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g== +undici@^7.24.4: + version "7.25.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-7.25.0.tgz#7d72fc429a0421769ca2966fd07cac875c85b781" + integrity sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"