mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
4070abadde
Only create new tab in `CheckAndFixWindow` if no tabs or pinned tabs exist Update `resolvers.resolveTabNum` to account for pinned tabs Remove obsolete and unused `wstore.DeleteTab` Only show accelerators for first 9 workspaces in workspace app menu to be consistent with other keybindings Fix tabbar spacing to remove min size for drag right spacer, account for workspace switcher button size Fix updatebanner size calculations
378 lines
11 KiB
TypeScript
378 lines
11 KiB
TypeScript
// Copyright 2024, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import { waveEventSubscribe } from "@/app/store/wps";
|
|
import { RpcApi } from "@/app/store/wshclientapi";
|
|
import * as electron from "electron";
|
|
import { fireAndForget } from "../frontend/util/util";
|
|
import { clearTabCache } from "./emain-tabview";
|
|
import {
|
|
createNewWaveWindow,
|
|
createWorkspace,
|
|
focusedWaveWindow,
|
|
getWaveWindowByWorkspaceId,
|
|
relaunchBrowserWindows,
|
|
WaveBrowserWindow,
|
|
} from "./emain-window";
|
|
import { ElectronWshClient } from "./emain-wsh";
|
|
import { unamePlatform } from "./platform";
|
|
import { updater } from "./updater";
|
|
|
|
type AppMenuCallbacks = {
|
|
createNewWaveWindow: () => Promise<void>;
|
|
relaunchBrowserWindows: () => Promise<void>;
|
|
};
|
|
|
|
function getWindowWebContents(window: electron.BaseWindow): electron.WebContents {
|
|
if (window == null) {
|
|
return null;
|
|
}
|
|
if (window instanceof electron.BaseWindow) {
|
|
const waveWin = window as WaveBrowserWindow;
|
|
if (waveWin.activeTabView) {
|
|
return waveWin.activeTabView.webContents;
|
|
}
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function getWorkspaceMenu(ww?: WaveBrowserWindow): Promise<Electron.MenuItemConstructorOptions[]> {
|
|
const workspaceList = await RpcApi.WorkspaceListCommand(ElectronWshClient);
|
|
console.log("workspaceList:", workspaceList);
|
|
const workspaceMenu: Electron.MenuItemConstructorOptions[] = [
|
|
{
|
|
label: "Create New Workspace",
|
|
click: (_, window) => {
|
|
fireAndForget(() => createWorkspace((window as WaveBrowserWindow) ?? ww));
|
|
},
|
|
},
|
|
];
|
|
function getWorkspaceSwitchAccelerator(i: number): string {
|
|
if (i < 9) {
|
|
return unamePlatform == "darwin" ? `Command+Control+${i}` : `Alt+Control+${i + 1}`;
|
|
}
|
|
}
|
|
workspaceList?.length &&
|
|
workspaceMenu.push(
|
|
{ type: "separator" },
|
|
...workspaceList.map<Electron.MenuItemConstructorOptions>((workspace, i) => {
|
|
return {
|
|
label: `Switch to ${workspace.workspacedata.name}`,
|
|
click: (_, window) => {
|
|
((window as WaveBrowserWindow) ?? ww)?.switchWorkspace(workspace.workspacedata.oid);
|
|
},
|
|
accelerator: getWorkspaceSwitchAccelerator(i),
|
|
};
|
|
})
|
|
);
|
|
return workspaceMenu;
|
|
}
|
|
|
|
async function getAppMenu(callbacks: AppMenuCallbacks, workspaceId?: string): Promise<Electron.Menu> {
|
|
const ww = workspaceId && getWaveWindowByWorkspaceId(workspaceId);
|
|
const fileMenu: Electron.MenuItemConstructorOptions[] = [
|
|
{
|
|
label: "New Window",
|
|
accelerator: "CommandOrControl+Shift+N",
|
|
click: () => fireAndForget(callbacks.createNewWaveWindow),
|
|
},
|
|
{
|
|
role: "close",
|
|
accelerator: "", // clear the accelerator
|
|
click: () => {
|
|
focusedWaveWindow?.close();
|
|
},
|
|
},
|
|
];
|
|
const appMenu: Electron.MenuItemConstructorOptions[] = [
|
|
{
|
|
label: "About Wave Terminal",
|
|
click: (_, window) => {
|
|
getWindowWebContents(window ?? ww)?.send("menu-item-about");
|
|
},
|
|
},
|
|
{
|
|
label: "Check for Updates",
|
|
click: () => {
|
|
fireAndForget(() => updater?.checkForUpdates(true));
|
|
},
|
|
},
|
|
{
|
|
type: "separator",
|
|
},
|
|
];
|
|
if (unamePlatform === "darwin") {
|
|
appMenu.push(
|
|
{
|
|
role: "services",
|
|
},
|
|
{
|
|
type: "separator",
|
|
},
|
|
{
|
|
role: "hide",
|
|
},
|
|
{
|
|
role: "hideOthers",
|
|
},
|
|
{
|
|
type: "separator",
|
|
}
|
|
);
|
|
}
|
|
appMenu.push({
|
|
role: "quit",
|
|
});
|
|
const editMenu: Electron.MenuItemConstructorOptions[] = [
|
|
{
|
|
role: "undo",
|
|
accelerator: unamePlatform === "darwin" ? "Command+Z" : "",
|
|
},
|
|
{
|
|
role: "redo",
|
|
accelerator: unamePlatform === "darwin" ? "Command+Shift+Z" : "",
|
|
},
|
|
{
|
|
type: "separator",
|
|
},
|
|
{
|
|
role: "cut",
|
|
accelerator: unamePlatform === "darwin" ? "Command+X" : "",
|
|
},
|
|
{
|
|
role: "copy",
|
|
accelerator: unamePlatform === "darwin" ? "Command+C" : "",
|
|
},
|
|
{
|
|
role: "paste",
|
|
accelerator: unamePlatform === "darwin" ? "Command+V" : "",
|
|
},
|
|
{
|
|
role: "pasteAndMatchStyle",
|
|
accelerator: unamePlatform === "darwin" ? "Command+Shift+V" : "",
|
|
},
|
|
{
|
|
role: "delete",
|
|
},
|
|
{
|
|
role: "selectAll",
|
|
accelerator: unamePlatform === "darwin" ? "Command+A" : "",
|
|
},
|
|
];
|
|
|
|
const devToolsAccel = unamePlatform === "darwin" ? "Option+Command+I" : "Alt+Shift+I";
|
|
const viewMenu: Electron.MenuItemConstructorOptions[] = [
|
|
{
|
|
label: "Reload Tab",
|
|
accelerator: "Shift+CommandOrControl+R",
|
|
click: (_, window) => {
|
|
getWindowWebContents(window ?? ww)?.reloadIgnoringCache();
|
|
},
|
|
},
|
|
{
|
|
label: "Relaunch All Windows",
|
|
click: () => {
|
|
callbacks.relaunchBrowserWindows();
|
|
},
|
|
},
|
|
{
|
|
label: "Clear Tab Cache",
|
|
click: () => {
|
|
clearTabCache();
|
|
},
|
|
},
|
|
{
|
|
label: "Toggle DevTools",
|
|
accelerator: devToolsAccel,
|
|
click: (_, window) => {
|
|
let wc = getWindowWebContents(window ?? ww);
|
|
wc?.toggleDevTools();
|
|
},
|
|
},
|
|
{
|
|
type: "separator",
|
|
},
|
|
{
|
|
label: "Reset Zoom",
|
|
accelerator: "CommandOrControl+0",
|
|
click: (_, window) => {
|
|
getWindowWebContents(window ?? ww)?.setZoomFactor(1);
|
|
},
|
|
},
|
|
{
|
|
label: "Zoom In",
|
|
accelerator: "CommandOrControl+=",
|
|
click: (_, window) => {
|
|
const wc = getWindowWebContents(window ?? ww);
|
|
if (wc == null) {
|
|
return;
|
|
}
|
|
if (wc.getZoomFactor() >= 5) {
|
|
return;
|
|
}
|
|
wc.setZoomFactor(wc.getZoomFactor() + 0.2);
|
|
},
|
|
},
|
|
{
|
|
label: "Zoom In (hidden)",
|
|
accelerator: "CommandOrControl+Shift+=",
|
|
click: (_, window) => {
|
|
const wc = getWindowWebContents(window ?? ww);
|
|
if (wc == null) {
|
|
return;
|
|
}
|
|
if (wc.getZoomFactor() >= 5) {
|
|
return;
|
|
}
|
|
wc.setZoomFactor(wc.getZoomFactor() + 0.2);
|
|
},
|
|
visible: false,
|
|
acceleratorWorksWhenHidden: true,
|
|
},
|
|
{
|
|
label: "Zoom Out",
|
|
accelerator: "CommandOrControl+-",
|
|
click: (_, window) => {
|
|
const wc = getWindowWebContents(window ?? ww);
|
|
if (wc == null) {
|
|
return;
|
|
}
|
|
if (wc.getZoomFactor() <= 0.2) {
|
|
return;
|
|
}
|
|
wc.setZoomFactor(wc.getZoomFactor() - 0.2);
|
|
},
|
|
},
|
|
{
|
|
label: "Zoom Out (hidden)",
|
|
accelerator: "CommandOrControl+Shift+-",
|
|
click: (_, window) => {
|
|
const wc = getWindowWebContents(window ?? ww);
|
|
if (wc == null) {
|
|
return;
|
|
}
|
|
if (wc.getZoomFactor() <= 0.2) {
|
|
return;
|
|
}
|
|
wc.setZoomFactor(wc.getZoomFactor() - 0.2);
|
|
},
|
|
visible: false,
|
|
acceleratorWorksWhenHidden: true,
|
|
},
|
|
{
|
|
type: "separator",
|
|
},
|
|
{
|
|
role: "togglefullscreen",
|
|
},
|
|
];
|
|
let workspaceMenu: Electron.MenuItemConstructorOptions[] = null;
|
|
try {
|
|
workspaceMenu = await getWorkspaceMenu();
|
|
} catch (e) {
|
|
console.error("getWorkspaceMenu error:", e);
|
|
}
|
|
const windowMenu: Electron.MenuItemConstructorOptions[] = [
|
|
{ role: "minimize", accelerator: "" },
|
|
{ role: "zoom" },
|
|
{ type: "separator" },
|
|
{ role: "front" },
|
|
{ type: "separator" },
|
|
{ role: "window" },
|
|
];
|
|
const menuTemplate: Electron.MenuItemConstructorOptions[] = [
|
|
{
|
|
role: "appMenu",
|
|
submenu: appMenu,
|
|
},
|
|
{
|
|
role: "fileMenu",
|
|
submenu: fileMenu,
|
|
},
|
|
{
|
|
role: "editMenu",
|
|
submenu: editMenu,
|
|
},
|
|
{
|
|
role: "viewMenu",
|
|
submenu: viewMenu,
|
|
},
|
|
];
|
|
if (workspaceMenu != null) {
|
|
menuTemplate.push({
|
|
label: "Workspace",
|
|
id: "workspace-menu",
|
|
submenu: workspaceMenu,
|
|
});
|
|
}
|
|
menuTemplate.push({
|
|
role: "windowMenu",
|
|
submenu: windowMenu,
|
|
});
|
|
return electron.Menu.buildFromTemplate(menuTemplate);
|
|
}
|
|
|
|
export function instantiateAppMenu(workspaceId?: string): Promise<electron.Menu> {
|
|
return getAppMenu(
|
|
{
|
|
createNewWaveWindow,
|
|
relaunchBrowserWindows,
|
|
},
|
|
workspaceId
|
|
);
|
|
}
|
|
|
|
export function makeAppMenu() {
|
|
fireAndForget(async () => {
|
|
const menu = await instantiateAppMenu();
|
|
electron.Menu.setApplicationMenu(menu);
|
|
});
|
|
}
|
|
|
|
waveEventSubscribe({
|
|
eventType: "workspace:update",
|
|
handler: makeAppMenu,
|
|
});
|
|
|
|
function convertMenuDefArrToMenu(workspaceId: string, menuDefArr: ElectronContextMenuItem[]): electron.Menu {
|
|
const menuItems: electron.MenuItem[] = [];
|
|
for (const menuDef of menuDefArr) {
|
|
const menuItemTemplate: electron.MenuItemConstructorOptions = {
|
|
role: menuDef.role as any,
|
|
label: menuDef.label,
|
|
type: menuDef.type,
|
|
click: (_, window) => {
|
|
const ww = (window as WaveBrowserWindow) ?? getWaveWindowByWorkspaceId(workspaceId);
|
|
if (!ww) {
|
|
console.error("invalid window for context menu click handler:", ww, window, workspaceId);
|
|
return;
|
|
}
|
|
ww?.activeTabView?.webContents?.send("contextmenu-click", menuDef.id);
|
|
},
|
|
checked: menuDef.checked,
|
|
};
|
|
if (menuDef.submenu != null) {
|
|
menuItemTemplate.submenu = convertMenuDefArrToMenu(workspaceId, menuDef.submenu);
|
|
}
|
|
const menuItem = new electron.MenuItem(menuItemTemplate);
|
|
menuItems.push(menuItem);
|
|
}
|
|
return electron.Menu.buildFromTemplate(menuItems);
|
|
}
|
|
|
|
electron.ipcMain.on("contextmenu-show", (event, workspaceId: string, menuDefArr?: ElectronContextMenuItem[]) => {
|
|
if (menuDefArr?.length === 0) {
|
|
return;
|
|
}
|
|
fireAndForget(async () => {
|
|
const menu = menuDefArr
|
|
? convertMenuDefArrToMenu(workspaceId, menuDefArr)
|
|
: await instantiateAppMenu(workspaceId);
|
|
menu.popup();
|
|
});
|
|
event.returnValue = true;
|
|
});
|
|
|
|
export { getAppMenu };
|