diff --git a/src/features/application-menu/main/application-menu-items.injectable.ts b/src/features/application-menu/main/application-menu-items.injectable.ts index 8a7d80550c..b6771d8faf 100644 --- a/src/features/application-menu/main/application-menu-items.injectable.ts +++ b/src/features/application-menu/main/application-menu-items.injectable.ts @@ -3,27 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { docsUrl, supportUrl } from "../../../common/vars"; -import { broadcastMessage } from "../../../common/ipc"; import type { MenuItemConstructorOptions } from "electron"; -import { webContents } from "electron"; -import loggerInjectable from "../../../common/logger.injectable"; -import electronMenuItemsInjectable from "./electron-menu-items.injectable"; -import updatingIsEnabledInjectable from "../../application-update/main/updating-is-enabled/updating-is-enabled.injectable"; -import navigateToPreferencesInjectable from "../../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable"; -import navigateToExtensionsInjectable from "../../../common/front-end-routing/routes/extensions/navigate-to-extensions.injectable"; -import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; -import navigateToWelcomeInjectable from "../../../common/front-end-routing/routes/welcome/navigate-to-welcome.injectable"; -import navigateToAddClusterInjectable from "../../../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable"; -import stopServicesAndExitAppInjectable from "../../../main/stop-services-and-exit-app.injectable"; -import isMacInjectable from "../../../common/vars/is-mac.injectable"; import { computed } from "mobx"; -import showAboutInjectable from "./menu-items/primary-for-mac/show-about-application/show-about.injectable"; -import reloadCurrentApplicationWindowInjectable from "../../../main/start-main-application/lens-window/reload-current-application-window.injectable"; -import showApplicationWindowInjectable from "../../../main/start-main-application/lens-window/show-application-window.injectable"; -import processCheckingForUpdatesInjectable from "../../application-update/main/process-checking-for-updates.injectable"; -import openLinkInBrowserInjectable from "../../../common/utils/open-link-in-browser.injectable"; -import productNameInjectable from "../../../common/vars/product-name.injectable"; import type { ApplicationMenuItemTypes } from "./menu-items/application-menu-item-injection-token"; import applicationMenuItemInjectionToken from "./menu-items/application-menu-item-injection-token"; import { filter, map, sortBy } from "lodash/fp"; @@ -31,10 +12,6 @@ import { pipeline } from "@ogre-tools/fp"; import type { Composite } from "./menu-items/get-composite/get-composite"; import getComposite from "./menu-items/get-composite/get-composite"; -function ignoreIf(check: boolean, menuItems: MenuItemOpts[]) { - return check ? [] : menuItems; -} - export interface MenuItemOpts extends MenuItemConstructorOptions { submenu?: MenuItemConstructorOptions[]; } @@ -43,35 +20,6 @@ const applicationMenuItemsInjectable = getInjectable({ id: "application-menu-items", instantiate: (di) => { - const logger = di.inject(loggerInjectable); - - const productName = di.inject(productNameInjectable); - const isMac = di.inject(isMacInjectable); - const updatingIsEnabled = di.inject(updatingIsEnabledInjectable); - const electronMenuItems = di.inject(electronMenuItemsInjectable); - const showAbout = di.inject(showAboutInjectable); - const showApplicationWindow = di.inject(showApplicationWindowInjectable); - - const reloadApplicationWindow = di.inject( - reloadCurrentApplicationWindowInjectable, - ); - - const navigateToPreferences = di.inject(navigateToPreferencesInjectable); - const navigateToExtensions = di.inject(navigateToExtensionsInjectable); - const navigateToCatalog = di.inject(navigateToCatalogInjectable); - const navigateToWelcome = di.inject(navigateToWelcomeInjectable); - const navigateToAddCluster = di.inject(navigateToAddClusterInjectable); - const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable); - - const processCheckingForUpdates = di.inject( - processCheckingForUpdatesInjectable, - ); - - const openLinkInBrowser = di.inject(openLinkInBrowserInjectable); - - // Todo: find out what to do with this. - // logger.info(`[MENU]: autoUpdateEnabled=${updatingIsEnabled}`); - const allShown = pipeline( di.injectMany(applicationMenuItemInjectionToken), filter((x) => x.isShown !== false), @@ -121,218 +69,25 @@ const applicationMenuItemsInjectable = getInjectable({ ); return computed((): MenuItemOpts[] => { - const fileMenu: MenuItemOpts = { - label: "File", - id: "file", - submenu: [ - { - label: "Add Cluster", - accelerator: "CmdOrCtrl+Shift+A", - id: "add-cluster", - click() { - navigateToAddCluster(); - }, - }, - ...ignoreIf(isMac, [ - { type: "separator" }, - { - label: "Preferences", - id: "preferences", - accelerator: "Ctrl+,", - click() { - navigateToPreferences(); - }, - }, - { - label: "Extensions", - accelerator: "Ctrl+Shift+E", - click() { - navigateToExtensions(); - }, - }, - ]), - { type: "separator" }, - ...ignoreIf(!isMac, [ - { - role: "close", - label: "Close Window", - accelerator: "Shift+Cmd+W", - }, - ]), - ...ignoreIf(isMac, [ - { - label: "Exit", - accelerator: "Alt+F4", - id: "quit", - click() { - stopServicesAndExitApp(); - }, - }, - ]), - ], - }; - const editMenu: MenuItemOpts = { - label: "Edit", - id: "edit", - submenu: [ - { role: "undo" }, - { role: "redo" }, - { type: "separator" }, - { role: "cut" }, - { role: "copy" }, - { role: "paste" }, - { role: "delete" }, - { type: "separator" }, - { role: "selectAll" }, - ], - }; - const viewMenu: MenuItemOpts = { - label: "View", - id: "view", - submenu: [ - { - label: "Catalog", - accelerator: "Shift+CmdOrCtrl+C", - id: "catalog", - click() { - navigateToCatalog(); - }, - }, - { - label: "Command Palette...", - accelerator: "Shift+CmdOrCtrl+P", - id: "command-palette", - click(_m, _b, event) { - /** - * Don't broadcast unless it was triggered by menu iteration so that - * there aren't double events in renderer - * - * NOTE: this `?` is required because of a bug in playwright. https://github.com/microsoft/playwright/issues/10554 - */ - if (!event?.triggeredByAccelerator) { - broadcastMessage("command-palette:open"); - } - }, - }, - { type: "separator" }, - { - label: "Back", - accelerator: "CmdOrCtrl+[", - id: "go-back", - click() { - webContents - .getAllWebContents() - .filter((wc) => wc.getType() === "window") - .forEach((wc) => wc.goBack()); - }, - }, - { - label: "Forward", - accelerator: "CmdOrCtrl+]", - id: "go-forward", - click() { - webContents - .getAllWebContents() - .filter((wc) => wc.getType() === "window") - .forEach((wc) => wc.goForward()); - }, - }, - { - label: "Reload", - accelerator: "CmdOrCtrl+R", - id: "reload", - click() { - reloadApplicationWindow(); - }, - }, - { role: "toggleDevTools" }, - { type: "separator" }, - { role: "resetZoom" }, - { role: "zoomIn" }, - { role: "zoomOut" }, - { type: "separator" }, - { role: "togglefullscreen" }, - ], - }; - const helpMenu: MenuItemOpts = { - role: "help", - id: "help", - submenu: [ - { - label: "Welcome", - id: "welcome", - click() { - navigateToWelcome(); - }, - }, - { - label: "Documentation", - id: "documentation", - click: async () => { - openLinkInBrowser(docsUrl).catch((error) => { - logger.error("[MENU]: failed to open browser", { error }); - }); - }, - }, - { - label: "Support", - id: "support", - click: async () => { - openLinkInBrowser(supportUrl).catch((error) => { - logger.error("[MENU]: failed to open browser", { error }); - }); - }, - }, - ...ignoreIf(isMac, [ - { - label: `About ${productName}`, - id: "about", - click() { - showAbout(); - }, - }, - ...ignoreIf(!updatingIsEnabled, [ - { - label: "Check for updates", - click() { - processCheckingForUpdates("periodic").then(() => - showApplicationWindow(), - ); - }, - }, - ]), - ]), - ], - }; // Prepare menu items order - const appMenu = new Map([ - ["file", fileMenu], - ["edit", editMenu], - ["view", viewMenu], - ["help", helpMenu], - ]); - // Modify menu from extensions-api - for (const menuItem of electronMenuItems.get()) { - const parentMenu = appMenu.get(menuItem.parentId); + // // Modify menu from extensions-api + // for (const menuItem of electronMenuItems.get()) { + // const parentMenu = appMenu.get(menuItem.parentId); + // + // if (!parentMenu) { + // logger.error( + // `[MENU]: cannot register menu item for parentId=${menuItem.parentId}, parent item doesn't exist`, + // { menuItem }, + // ); + // + // continue; + // } + // + // // (parentMenu.submenu ??= []).push(menuItem); + // } - if (!parentMenu) { - logger.error( - `[MENU]: cannot register menu item for parentId=${menuItem.parentId}, parent item doesn't exist`, - { menuItem }, - ); - - continue; - } - - (parentMenu.submenu ??= []).push(menuItem); - } - - if (!isMac) { - appMenu.delete("mac"); - } - - return [...menuItems, ...appMenu.values()]; + return menuItems; }); }, }); diff --git a/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts b/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts index 11096731c2..5063c14b86 100644 --- a/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts +++ b/src/features/application-menu/main/menu-items/application-menu-item-injection-token.ts @@ -3,6 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectionToken } from "@ogre-tools/injectable"; +import type { BrowserWindow, MenuItem, KeyboardEvent } from "electron"; interface Shared { parentId: string | null; @@ -11,10 +12,16 @@ interface Shared { } export interface ApplicationMenuItem extends Shared { - id: string; label: string; - click?: () => void; accelerator?: string; + id: string; + + // TODO: This leaky abstraction is exposed in Extension API, therefore cannot be updated + click?: ( + menuItem: MenuItem, + browserWindow: BrowserWindow | undefined, + event: KeyboardEvent + ) => void; } export interface Separator extends Shared { @@ -22,13 +29,37 @@ export interface Separator extends Shared { } export interface OperationSystemAction extends Shared { - role: "services" | "hide" | "hideOthers" | "unhide"; + label?: string; + accelerator?: string; + + role: + | "services" + | "hide" + | "hideOthers" + | "unhide" + | "close" + | "undo" + | "redo" + | "cut" + | "copy" + | "paste" + | "delete" + | "selectAll" + | "toggleDevTools" + | "resetZoom" + | "zoomIn" + | "zoomOut" + | "togglefullscreen"; } -export type ApplicationMenuItemTypes = ApplicationMenuItem | Separator | OperationSystemAction; +export type ApplicationMenuItemTypes = + | ApplicationMenuItem + | Separator + | OperationSystemAction; -const applicationMenuItemInjectionToken = getInjectionToken({ - id: "application-menu-item-injection-token", -}); +const applicationMenuItemInjectionToken = + getInjectionToken({ + id: "application-menu-item-injection-token", + }); export default applicationMenuItemInjectionToken; diff --git a/src/features/application-menu/main/menu-items/edit/edit-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/edit/edit-menu-item.injectable.ts new file mode 100644 index 0000000000..278faf7604 --- /dev/null +++ b/src/features/application-menu/main/menu-items/edit/edit-menu-item.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../application-menu-item-injection-token"; + +const editMenuItemInjectable = getInjectable({ + id: "edit-application-menu-item", + + instantiate: () => ({ + parentId: null, + id: "edit", + orderNumber: 30, + label: "Edit", + }), + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default editMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/edit/operation-system-actions/operation-system-actions.injectable.ts b/src/features/application-menu/main/menu-items/edit/operation-system-actions/operation-system-actions.injectable.ts new file mode 100644 index 0000000000..64de60401f --- /dev/null +++ b/src/features/application-menu/main/menu-items/edit/operation-system-actions/operation-system-actions.injectable.ts @@ -0,0 +1,73 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { + getApplicationMenuOperationSystemActionInjectable, +} from "../../get-application-menu-operation-system-action-injectable"; +import { + getApplicationMenuSeparatorInjectable, +} from "../../get-application-menu-separator-injectable"; + +export const actionForUndo = getApplicationMenuOperationSystemActionInjectable({ + id: "undo", + parentId: "edit", + orderNumber: 10, + role: "undo", +}); + +export const actionForRedo = getApplicationMenuOperationSystemActionInjectable({ + id: "redo", + parentId: "edit", + orderNumber: 20, + role: "redo", +}); + +export const separator1 = getApplicationMenuSeparatorInjectable({ + id: "separator-1-in-edit", + parentId: "edit", + orderNumber: 30, +}); + +export const actionForCut = getApplicationMenuOperationSystemActionInjectable({ + id: "cut", + parentId: "edit", + orderNumber: 40, + role: "cut", +}); + +export const actionForCopy = getApplicationMenuOperationSystemActionInjectable({ + id: "copy", + parentId: "edit", + orderNumber: 50, + role: "copy", +}); + +export const actionForPaste = getApplicationMenuOperationSystemActionInjectable({ + id: "paste", + parentId: "edit", + orderNumber: 60, + role: "paste", +}); + +export const actionForDelete = getApplicationMenuOperationSystemActionInjectable({ + id: "delete", + parentId: "edit", + orderNumber: 70, + role: "delete", +}); + +export const separator2 = getApplicationMenuSeparatorInjectable({ + id: "separator-2-in-edit", + parentId: "edit", + orderNumber: 80, +}); + +export const actionForSelectAll = getApplicationMenuOperationSystemActionInjectable({ + id: "selectAll", + parentId: "edit", + orderNumber: 90, + role: "selectAll", +}); + diff --git a/src/features/application-menu/main/menu-items/file/add-cluster/add-cluster-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/file/add-cluster/add-cluster-menu-item.injectable.ts new file mode 100644 index 0000000000..bc3b3c8ffd --- /dev/null +++ b/src/features/application-menu/main/menu-items/file/add-cluster/add-cluster-menu-item.injectable.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import navigateToAddClusterInjectable from "../../../../../../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable"; + +const addClusterMenuItemInjectable = getInjectable({ + id: "add-cluster-application-menu-item", + + instantiate: (di) => { + const navigateToAddCluster = di.inject(navigateToAddClusterInjectable); + + return { + parentId: "file", + id: "add-cluster", + orderNumber: 10, + label: "Add Cluster", + accelerator: "CmdOrCtrl+Shift+A", + + click: () => { + navigateToAddCluster(); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default addClusterMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/file/close-window/close-window-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/file/close-window/close-window-menu-item.injectable.ts new file mode 100644 index 0000000000..2173b2c233 --- /dev/null +++ b/src/features/application-menu/main/menu-items/file/close-window/close-window-menu-item.injectable.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import isMacInjectable from "../../../../../../common/vars/is-mac.injectable"; + +const closeWindowMenuItemInjectable = getInjectable({ + id: "close-window-application-menu-item", + + instantiate: (di) => { + const isMac = di.inject(isMacInjectable); + + return { + parentId: "file", + orderNumber: 60, + role: "close" as const, + label: "Close Window", + accelerator: "Shift+Cmd+W", + isShown: isMac, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default closeWindowMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/file/file-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/file/file-menu-item.injectable.ts new file mode 100644 index 0000000000..930ed662a3 --- /dev/null +++ b/src/features/application-menu/main/menu-items/file/file-menu-item.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../application-menu-item-injection-token"; + +const fileMenuItemInjectable = getInjectable({ + id: "file-application-menu-item", + + instantiate: () => ({ + parentId: null, + id: "file", + orderNumber: 20, + label: "File", + }), + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default fileMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/file/separators/separators.injectable.ts b/src/features/application-menu/main/menu-items/file/separators/separators.injectable.ts new file mode 100644 index 0000000000..4f5e819bf7 --- /dev/null +++ b/src/features/application-menu/main/menu-items/file/separators/separators.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { + getApplicationMenuSeparatorInjectable, +} from "../../get-application-menu-separator-injectable"; + +export const separator1 = getApplicationMenuSeparatorInjectable({ + id: "separator-1-for-file", + parentId: "file", + orderNumber: 20, + isShownOnlyOnMac: true, +}); + +export const separator2 = getApplicationMenuSeparatorInjectable({ + id: "separator-2-for-file", + parentId: "file", + orderNumber: 50, +}); diff --git a/src/features/application-menu/main/menu-items/get-application-menu-separator-injectable.ts b/src/features/application-menu/main/menu-items/get-application-menu-separator-injectable.ts index 0725565711..05f6905614 100644 --- a/src/features/application-menu/main/menu-items/get-application-menu-separator-injectable.ts +++ b/src/features/application-menu/main/menu-items/get-application-menu-separator-injectable.ts @@ -5,15 +5,26 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { Separator } from "./application-menu-item-injection-token"; import applicationMenuItemInjectionToken from "./application-menu-item-injection-token"; +import isMacInjectable from "../../../../common/vars/is-mac.injectable"; -const getApplicationMenuSeparatorInjectable = ({ id, ...rest }: { id: string } & Omit) => +const getApplicationMenuSeparatorInjectable = ({ + id, + isShownOnlyOnMac = false, + ...rest +}: { id: string; isShownOnlyOnMac?: boolean } & Omit) => getInjectable({ id: `application-menu-separator/${id}`, - instantiate: () => ({ - ...rest, - type: "separator" as const, - }), + instantiate: (di) => { + const isMac = di.inject(isMacInjectable); + const isShown = isShownOnlyOnMac ? isMac : true; + + return { + ...rest, + isShown, + type: "separator" as const, + }; + }, injectionToken: applicationMenuItemInjectionToken, }); diff --git a/src/features/application-menu/main/menu-items/help/help-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/help/help-menu-item.injectable.ts new file mode 100644 index 0000000000..01b213bda7 --- /dev/null +++ b/src/features/application-menu/main/menu-items/help/help-menu-item.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../application-menu-item-injection-token"; + +const helpMenuItemInjectable = getInjectable({ + id: "help-application-menu-item", + + instantiate: () => ({ + parentId: null, + id: "help", + orderNumber: 50, + label: "Help", + }), + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default helpMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/help/navigate-to-welcome/navigate-to-extensions-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/help/navigate-to-welcome/navigate-to-extensions-menu-item.injectable.ts new file mode 100644 index 0000000000..4e687a6e1b --- /dev/null +++ b/src/features/application-menu/main/menu-items/help/navigate-to-welcome/navigate-to-extensions-menu-item.injectable.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import navigateToWelcomeInjectable from "../../../../../../common/front-end-routing/routes/welcome/navigate-to-welcome.injectable"; + +const navigateToWelcomeMenuItem = getInjectable({ + id: "navigate-to-welcome-menu-item", + + instantiate: (di) => { + const navigateToWelcome = di.inject(navigateToWelcomeInjectable); + + return { + parentId: "help", + id: "navigate-to-welcome", + orderNumber: 10, + label: "Welcome", + + click: () => { + navigateToWelcome(); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default navigateToWelcomeMenuItem; diff --git a/src/features/application-menu/main/menu-items/help/open-documentation/open-documentation-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/help/open-documentation/open-documentation-menu-item.injectable.ts new file mode 100644 index 0000000000..5c0288fd10 --- /dev/null +++ b/src/features/application-menu/main/menu-items/help/open-documentation/open-documentation-menu-item.injectable.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import { docsUrl } from "../../../../../../common/vars"; +import openLinkInBrowserInjectable from "../../../../../../common/utils/open-link-in-browser.injectable"; +import loggerInjectable from "../../../../../../common/logger.injectable"; + +const openDocumentationMenuItemInjectable = getInjectable({ + id: "open-documentation-menu-item", + + instantiate: (di) => { + const openLinkInBrowser = di.inject(openLinkInBrowserInjectable); + const logger = di.inject(loggerInjectable); + + return { + parentId: "help", + id: "open-documentation", + orderNumber: 20, + label: "Documentation", + + // TODO: Convert to async/await + click: () => { + openLinkInBrowser(docsUrl).catch((error) => { + logger.error("[MENU]: failed to open browser", { error }); + }); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default openDocumentationMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/help/open-support/open-support-item.injectable.ts b/src/features/application-menu/main/menu-items/help/open-support/open-support-item.injectable.ts new file mode 100644 index 0000000000..c5e15bebfb --- /dev/null +++ b/src/features/application-menu/main/menu-items/help/open-support/open-support-item.injectable.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import { supportUrl } from "../../../../../../common/vars"; +import openLinkInBrowserInjectable from "../../../../../../common/utils/open-link-in-browser.injectable"; +import loggerInjectable from "../../../../../../common/logger.injectable"; + +const openSupportItemInjectable = getInjectable({ + id: "open-support-menu-item", + + instantiate: (di) => { + const openLinkInBrowser = di.inject(openLinkInBrowserInjectable); + const logger = di.inject(loggerInjectable); + + return { + parentId: "help", + id: "open-support", + orderNumber: 30, + label: "Support", + + // TODO: Convert to async/await + click: () => { + openLinkInBrowser(supportUrl).catch((error) => { + logger.error("[MENU]: failed to open browser", { error }); + }); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default openSupportItemInjectable; diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/check-for-updates/check-for-updates-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/check-for-updates/check-for-updates-menu-item.injectable.ts index 0a86605ad9..65afba8f8b 100644 --- a/src/features/application-menu/main/menu-items/primary-for-mac/check-for-updates/check-for-updates-menu-item.injectable.ts +++ b/src/features/application-menu/main/menu-items/primary-for-mac/check-for-updates/check-for-updates-menu-item.injectable.ts @@ -8,6 +8,7 @@ import applicationMenuItemInjectionToken from "../../application-menu-item-injec import processCheckingForUpdatesInjectable from "../../../../../application-update/main/process-checking-for-updates.injectable"; import showApplicationWindowInjectable from "../../../../../../main/start-main-application/lens-window/show-application-window.injectable"; import updatingIsEnabledInjectable from "../../../../../application-update/main/updating-is-enabled/updating-is-enabled.injectable"; +import isMacInjectable from "../../../../../../common/vars/is-mac.injectable"; const checkForUpdatesMenuItemInjectable = getInjectable({ id: "check-for-updates-menu-item", @@ -20,11 +21,12 @@ const checkForUpdatesMenuItemInjectable = getInjectable({ const showApplicationWindow = di.inject(showApplicationWindowInjectable); const updatingIsEnabled = di.inject(updatingIsEnabledInjectable); + const isMac = di.inject(isMacInjectable); return { - parentId: "primary-for-mac", id: "check-for-updates", - orderNumber: 20, + parentId: isMac ? "primary-for-mac" : "help", + orderNumber: isMac ? 20 : 50, label: "Check for updates", isShown: updatingIsEnabled, diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-extensions/navigate-to-extensions-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-extensions/navigate-to-extensions-menu-item.injectable.ts index 4908b3c216..2a443068bf 100644 --- a/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-extensions/navigate-to-extensions-menu-item.injectable.ts +++ b/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-extensions/navigate-to-extensions-menu-item.injectable.ts @@ -5,19 +5,21 @@ import { getInjectable } from "@ogre-tools/injectable"; import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; import navigateToExtensionsInjectable from "../../../../../../common/front-end-routing/routes/extensions/navigate-to-extensions.injectable"; +import isMacInjectable from "../../../../../../common/vars/is-mac.injectable"; const navigateToExtensionsMenuItem = getInjectable({ id: "navigate-to-extensions-menu-item", instantiate: (di) => { const navigateToExtensions = di.inject(navigateToExtensionsInjectable); + const isMac = di.inject(isMacInjectable); return { - parentId: "primary-for-mac", + parentId: isMac ? "primary-for-mac" : "file", id: "navigate-to-extensions", - orderNumber: 50, + orderNumber: isMac ? 50 : 40, label: "Extensions", - accelerator: "CmdOrCtrl+Shift+E", + accelerator: isMac ? "CmdOrCtrl+Shift+E" : "Ctrl+Shift+E", click: () => { navigateToExtensions(); diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-preferences/navigate-to-preferences-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-preferences/navigate-to-preferences-menu-item.injectable.ts index 098d831d54..7bda93d7af 100644 --- a/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-preferences/navigate-to-preferences-menu-item.injectable.ts +++ b/src/features/application-menu/main/menu-items/primary-for-mac/navigate-to-preferences/navigate-to-preferences-menu-item.injectable.ts @@ -5,19 +5,21 @@ import { getInjectable } from "@ogre-tools/injectable"; import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; import navigateToPreferencesInjectable from "../../../../../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable"; +import isMacInjectable from "../../../../../../common/vars/is-mac.injectable"; -const navigateToPreferencesMenuItem = getInjectable({ +const navigateToPreferencesMenuItemInjectable = getInjectable({ id: "navigate-to-preferences-menu-item", instantiate: (di) => { const navigateToPreferences = di.inject(navigateToPreferencesInjectable); + const isMac = di.inject(isMacInjectable); return { - parentId: "primary-for-mac", + parentId: isMac ? "primary-for-mac" : "file", id: "navigate-to-preferences", - orderNumber: 40, + orderNumber: isMac ? 40 : 30, label: "Preferences", - accelerator: "CmdOrCtrl+,", + accelerator: isMac ? "CmdOrCtrl+," : "Ctrl+,", click: () => { navigateToPreferences(); @@ -28,4 +30,4 @@ const navigateToPreferencesMenuItem = getInjectable({ injectionToken: applicationMenuItemInjectionToken, }); -export default navigateToPreferencesMenuItem; +export default navigateToPreferencesMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/quit-application/quit-application-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/quit-application/quit-application-menu-item.injectable.ts index 9538547ce4..fc7b44add5 100644 --- a/src/features/application-menu/main/menu-items/primary-for-mac/quit-application/quit-application-menu-item.injectable.ts +++ b/src/features/application-menu/main/menu-items/primary-for-mac/quit-application/quit-application-menu-item.injectable.ts @@ -5,19 +5,22 @@ import { getInjectable } from "@ogre-tools/injectable"; import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; import stopServicesAndExitAppInjectable from "../../../../../../main/stop-services-and-exit-app.injectable"; +import isMacInjectable from "../../../../../../common/vars/is-mac.injectable"; const quitApplicationMenuItemInjectable = getInjectable({ id: "quit-application-menu-item", instantiate: (di) => { const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable); + const isMac = di.inject(isMacInjectable); - return { + return { id: "quit", - parentId: "primary-for-mac", - orderNumber: 140, label: "Quit", - accelerator: "Cmd+Q", + + parentId: isMac ? "primary-for-mac" : "file", + orderNumber: isMac ? 140 : 70, + accelerator: isMac ? "Cmd+Q" : "Alt+F4", click: () => { stopServicesAndExitApp(); diff --git a/src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/about-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/about-menu-item.injectable.ts index 5511b0208d..f80171be6e 100644 --- a/src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/about-menu-item.injectable.ts +++ b/src/features/application-menu/main/menu-items/primary-for-mac/show-about-application/about-menu-item.injectable.ts @@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import productNameInjectable from "../../../../../../common/vars/product-name.injectable"; import showAboutInjectable from "./show-about.injectable"; import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import isMacInjectable from "../../../../../../common/vars/is-mac.injectable"; const aboutMenuItemInjectable = getInjectable({ id: "about-menu-item", @@ -13,11 +14,12 @@ const aboutMenuItemInjectable = getInjectable({ instantiate: (di) => { const productName = di.inject(productNameInjectable); const showAbout = di.inject(showAboutInjectable); + const isMac = di.inject(isMacInjectable); return { - parentId: "primary-for-mac", id: "about", - orderNumber: 10, + parentId: isMac ? "primary-for-mac" : "help", + orderNumber: isMac ? 10 : 40, label: `About ${productName}`, click() { diff --git a/src/features/application-menu/main/menu-items/view/go-back/go-back-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/view/go-back/go-back-menu-item.injectable.ts new file mode 100644 index 0000000000..f11df6f6e0 --- /dev/null +++ b/src/features/application-menu/main/menu-items/view/go-back/go-back-menu-item.injectable.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import { webContents } from "electron"; + +const goBackMenuItemInjectable = getInjectable({ + id: "go-back-menu-item", + + instantiate: () => ({ + parentId: "view", + id: "go-back", + orderNumber: 40, + label: "Back", + accelerator: "CmdOrCtrl+[", + + click: () => { + webContents + .getAllWebContents() + .filter((wc) => wc.getType() === "window") + .forEach((wc) => wc.goBack()); + }, + }), + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default goBackMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/view/go-forward/go-forward-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/view/go-forward/go-forward-menu-item.injectable.ts new file mode 100644 index 0000000000..796b2c00c1 --- /dev/null +++ b/src/features/application-menu/main/menu-items/view/go-forward/go-forward-menu-item.injectable.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import { webContents } from "electron"; + +const goForwardMenuItemInjectable = getInjectable({ + id: "go-forward-menu-item", + + instantiate: () => ({ + parentId: "view", + id: "go-forward", + orderNumber: 50, + label: "Forward", + accelerator: "CmdOrCtrl+]", + + click: () => { + webContents + .getAllWebContents() + .filter((wc) => wc.getType() === "window") + .forEach((wc) => wc.goForward()); + }, + }), + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default goForwardMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/view/navigate-to-catalog/navigate-to-catalog-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/view/navigate-to-catalog/navigate-to-catalog-menu-item.injectable.ts new file mode 100644 index 0000000000..a8053f1ddd --- /dev/null +++ b/src/features/application-menu/main/menu-items/view/navigate-to-catalog/navigate-to-catalog-menu-item.injectable.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import navigateToCatalogInjectable from "../../../../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; + +const navigateToCatalogMenuItemInjectable = getInjectable({ + id: "navigate-to-catalog-menu-item", + + instantiate: (di) => { + const navigateToCatalog = di.inject(navigateToCatalogInjectable); + + return { + parentId: "view", + id: "navigate-to-catalog", + orderNumber: 10, + label: "Catalog", + accelerator: "Shift+CmdOrCtrl+C", + + click: () => { + navigateToCatalog(); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default navigateToCatalogMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/view/open-command-palette/open-command-palette-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/view/open-command-palette/open-command-palette-menu-item.injectable.ts new file mode 100644 index 0000000000..622335eaea --- /dev/null +++ b/src/features/application-menu/main/menu-items/view/open-command-palette/open-command-palette-menu-item.injectable.ts @@ -0,0 +1,39 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import broadcastMessageInjectable from "../../../../../../common/ipc/broadcast-message.injectable"; + +const openCommandPaletteMenuItemInjectable = getInjectable({ + id: "open-command-palette-menu-item", + + instantiate: (di) => { + const broadcastMessage = di.inject(broadcastMessageInjectable); + + return { + parentId: "view", + id: "open-command-palette", + orderNumber: 20, + label: "Command Palette...", + accelerator: "Shift+CmdOrCtrl+P", + + click(_m, _b, event) { + /** + * Don't broadcast unless it was triggered by menu iteration so that + * there aren't double events in renderer + * + * NOTE: this `?` is required because of a bug in playwright. https://github.com/microsoft/playwright/issues/10554 + */ + if (!event?.triggeredByAccelerator) { + broadcastMessage("command-palette:open"); + } + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default openCommandPaletteMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/view/operation-system-actions/operation-system-actions.injectable.ts b/src/features/application-menu/main/menu-items/view/operation-system-actions/operation-system-actions.injectable.ts new file mode 100644 index 0000000000..a994439d87 --- /dev/null +++ b/src/features/application-menu/main/menu-items/view/operation-system-actions/operation-system-actions.injectable.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { + getApplicationMenuOperationSystemActionInjectable, +} from "../../get-application-menu-operation-system-action-injectable"; + +export const actionForToggleDevTools = getApplicationMenuOperationSystemActionInjectable({ + id: "toggle-dev-tools", + parentId: "view", + orderNumber: 70, + role: "toggleDevTools", +}); + +export const actionForResetZoom = getApplicationMenuOperationSystemActionInjectable({ + id: "reset-zoom", + parentId: "view", + orderNumber: 90, + role: "resetZoom", +}); + +export const actionForZoomIn = getApplicationMenuOperationSystemActionInjectable({ + id: "zoom-in", + parentId: "view", + orderNumber: 100, + role: "zoomIn", +}); + +export const actionForZoomOut = getApplicationMenuOperationSystemActionInjectable({ + id: "zoom-out", + parentId: "view", + orderNumber: 110, + role: "zoomOut", +}); + +export const actionForToggleFullScreen = getApplicationMenuOperationSystemActionInjectable({ + id: "toggle-full-screen", + parentId: "view", + orderNumber: 130, + role: "togglefullscreen", +}); diff --git a/src/features/application-menu/main/menu-items/view/reload/reload-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/view/reload/reload-menu-item.injectable.ts new file mode 100644 index 0000000000..4b1b094eb6 --- /dev/null +++ b/src/features/application-menu/main/menu-items/view/reload/reload-menu-item.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../../application-menu-item-injection-token"; +import reloadCurrentApplicationWindowInjectable from "../../../../../../main/start-main-application/lens-window/reload-current-application-window.injectable"; + +const reloadMenuItemInjectable = getInjectable({ + id: "reload-menu-item", + + instantiate: (di) => { + const reloadApplicationWindow = di.inject( + reloadCurrentApplicationWindowInjectable, + ); + + return { + parentId: "view", + id: "reload", + orderNumber: 60, + label: "Reload", + accelerator: "CmdOrCtrl+R", + + click: () => { + reloadApplicationWindow(); + }, + }; + }, + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default reloadMenuItemInjectable; diff --git a/src/features/application-menu/main/menu-items/view/separators/separators.injectable.ts b/src/features/application-menu/main/menu-items/view/separators/separators.injectable.ts new file mode 100644 index 0000000000..00584ea384 --- /dev/null +++ b/src/features/application-menu/main/menu-items/view/separators/separators.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { + getApplicationMenuSeparatorInjectable, +} from "../../get-application-menu-separator-injectable"; + +export const separator1 = getApplicationMenuSeparatorInjectable({ + id: "separator-1-for-view", + parentId: "view", + orderNumber: 30, +}); + +export const separator2 = getApplicationMenuSeparatorInjectable({ + id: "separator-2-for-view", + parentId: "view", + orderNumber: 80, +}); + +export const separator3 = getApplicationMenuSeparatorInjectable({ + id: "separator-3-for-view", + parentId: "view", + orderNumber: 120, +}); diff --git a/src/features/application-menu/main/menu-items/view/view-menu-item.injectable.ts b/src/features/application-menu/main/menu-items/view/view-menu-item.injectable.ts new file mode 100644 index 0000000000..103838b10c --- /dev/null +++ b/src/features/application-menu/main/menu-items/view/view-menu-item.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import applicationMenuItemInjectionToken from "../application-menu-item-injection-token"; + +const viewMenuItemInjectable = getInjectable({ + id: "view-application-menu-item", + + instantiate: () => ({ + parentId: null, + id: "view", + orderNumber: 40, + label: "View", + }), + + injectionToken: applicationMenuItemInjectionToken, +}); + +export default viewMenuItemInjectable;