源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
// Native application menu: template installation, the macOS/system menu
|
||||
// actions that forward into the renderer (settings, updates, sidebar, zoom),
|
||||
// and Windows/Linux menu-bar visibility. Extracted from main.mjs as a
|
||||
// factory (createRuntimeManager pattern); the NATIVE_MENU_* channels are
|
||||
// consumed by the preload bridge.
|
||||
import { BrowserWindow, Menu, shell } from "electron";
|
||||
import { runDetachedTask } from "./process-resilience.mjs";
|
||||
|
||||
const NATIVE_MENU_OPEN_SETTINGS_EVENT = "openwork:native-menu:open-settings";
|
||||
const NATIVE_MENU_TOGGLE_SIDEBAR_EVENT = "openwork:native-menu:toggle-sidebar";
|
||||
const NATIVE_MENU_CHECK_UPDATES_EVENT = "openwork:native-menu:check-updates";
|
||||
const NATIVE_MENU_ZOOM_EVENT = "openwork:native-menu:zoom";
|
||||
|
||||
export function createApplicationMenu({ appName, docsUrl, getWindow }) {
|
||||
let applicationMenuVisible = process.platform === "darwin";
|
||||
let currentAppName = appName;
|
||||
|
||||
async function openSettingsFromNativeMenu() {
|
||||
const win = await getWindow();
|
||||
if (win.isMinimized()) win.restore();
|
||||
win.show();
|
||||
win.focus();
|
||||
win.webContents.send(NATIVE_MENU_OPEN_SETTINGS_EVENT);
|
||||
}
|
||||
|
||||
async function checkForUpdatesFromNativeMenu() {
|
||||
const win = await getWindow();
|
||||
if (win.isMinimized()) win.restore();
|
||||
win.show();
|
||||
win.focus();
|
||||
win.webContents.send(NATIVE_MENU_CHECK_UPDATES_EVENT);
|
||||
}
|
||||
|
||||
async function toggleSidebarFromNativeMenu() {
|
||||
const win = await getWindow();
|
||||
win.webContents.send(NATIVE_MENU_TOGGLE_SIDEBAR_EVENT);
|
||||
}
|
||||
|
||||
// Zoom must flow through the renderer's font-zoom pathway so the persisted
|
||||
// preference and the applied webContents zoom factor never drift apart. The
|
||||
// built-in resetZoom/zoomIn/zoomOut roles bypass that pathway (and zoom
|
||||
// whichever webContents is focused, including the embedded browser view).
|
||||
async function zoomFromNativeMenu(action) {
|
||||
const win = await getWindow();
|
||||
win.webContents.send(NATIVE_MENU_ZOOM_EVENT, action);
|
||||
}
|
||||
|
||||
function install() {
|
||||
const isMac = process.platform === "darwin";
|
||||
const template = /** @type {import("electron").MenuItemConstructorOptions[]} */ ([
|
||||
...(isMac
|
||||
? [
|
||||
{
|
||||
label: currentAppName,
|
||||
submenu: [
|
||||
{ role: "about" },
|
||||
{
|
||||
label: "Check for Updates...",
|
||||
click: () => {
|
||||
runDetachedTask("check for updates from menu", checkForUpdatesFromNativeMenu);
|
||||
},
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Settings...",
|
||||
accelerator: "Command+,",
|
||||
click: () => {
|
||||
runDetachedTask("open settings from menu", openSettingsFromNativeMenu);
|
||||
},
|
||||
},
|
||||
{ type: "separator" },
|
||||
{ role: "services" },
|
||||
{ type: "separator" },
|
||||
{ role: "hide" },
|
||||
{ role: "hideOthers" },
|
||||
{ role: "unhide" },
|
||||
{ type: "separator" },
|
||||
{ role: "quit" },
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: "File",
|
||||
submenu: [
|
||||
...(isMac
|
||||
? []
|
||||
: [
|
||||
{
|
||||
label: "Settings",
|
||||
accelerator: "Control+,",
|
||||
click: () => {
|
||||
runDetachedTask("open settings from menu", openSettingsFromNativeMenu);
|
||||
},
|
||||
},
|
||||
{ type: "separator" },
|
||||
]),
|
||||
{ role: "close" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Edit",
|
||||
submenu: [
|
||||
{ role: "undo" },
|
||||
{ role: "redo" },
|
||||
{ type: "separator" },
|
||||
{ role: "cut" },
|
||||
{ role: "copy" },
|
||||
{ role: "paste" },
|
||||
...(isMac
|
||||
? [
|
||||
{ role: "pasteAndMatchStyle" },
|
||||
{ role: "delete" },
|
||||
{ role: "selectAll" },
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Speech",
|
||||
submenu: [
|
||||
{ role: "startSpeaking" },
|
||||
{ role: "stopSpeaking" },
|
||||
],
|
||||
},
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Settings...",
|
||||
click: () => {
|
||||
runDetachedTask("open settings from menu", openSettingsFromNativeMenu);
|
||||
},
|
||||
},
|
||||
]
|
||||
: [
|
||||
{ role: "delete" },
|
||||
{ type: "separator" },
|
||||
{ role: "selectAll" },
|
||||
]),
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "View",
|
||||
submenu: [
|
||||
{
|
||||
label: "Toggle Sidebar",
|
||||
accelerator: "CommandOrControl+B",
|
||||
click: () => {
|
||||
runDetachedTask("toggle sidebar from menu", toggleSidebarFromNativeMenu);
|
||||
},
|
||||
},
|
||||
{ type: "separator" },
|
||||
{ role: "reload" },
|
||||
{ role: "forceReload" },
|
||||
{ role: "toggleDevTools" },
|
||||
{ type: "separator" },
|
||||
{
|
||||
label: "Actual Size",
|
||||
accelerator: "CommandOrControl+0",
|
||||
click: () => {
|
||||
runDetachedTask("reset zoom from menu", () => zoomFromNativeMenu("reset"));
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Zoom In",
|
||||
accelerator: "CommandOrControl+Plus",
|
||||
click: () => {
|
||||
runDetachedTask("zoom in from menu", () => zoomFromNativeMenu("in"));
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Zoom Out",
|
||||
accelerator: "CommandOrControl+-",
|
||||
click: () => {
|
||||
runDetachedTask("zoom out from menu", () => zoomFromNativeMenu("out"));
|
||||
},
|
||||
},
|
||||
{ type: "separator" },
|
||||
{ role: "togglefullscreen" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Window",
|
||||
submenu: [
|
||||
{ role: "minimize" },
|
||||
{ role: "zoom" },
|
||||
...(isMac
|
||||
? [
|
||||
{ type: "separator" },
|
||||
{ role: "front" },
|
||||
{ type: "separator" },
|
||||
{ role: "window" },
|
||||
]
|
||||
: [
|
||||
{ role: "close" },
|
||||
]),
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "help",
|
||||
submenu: [
|
||||
...(isMac
|
||||
? []
|
||||
: [
|
||||
{
|
||||
label: "Check for Updates...",
|
||||
click: () => {
|
||||
runDetachedTask("check for updates from menu", checkForUpdatesFromNativeMenu);
|
||||
},
|
||||
},
|
||||
{ type: "separator" },
|
||||
]),
|
||||
{
|
||||
label: "Docs",
|
||||
click: () => {
|
||||
runDetachedTask("open documentation from menu", () => shell.openExternal(docsUrl));
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
||||
}
|
||||
|
||||
function applyVisibility(window) {
|
||||
if (process.platform === "darwin") return;
|
||||
window.setAutoHideMenuBar(false);
|
||||
window.setMenuBarVisibility(applicationMenuVisible);
|
||||
}
|
||||
|
||||
function setVisible(visible) {
|
||||
applicationMenuVisible = visible === true;
|
||||
for (const window of BrowserWindow.getAllWindows()) {
|
||||
applyVisibility(window);
|
||||
}
|
||||
return applicationMenuVisible;
|
||||
}
|
||||
|
||||
function setAppName(nextAppName) {
|
||||
currentAppName = typeof nextAppName === "string" && nextAppName.trim() ? nextAppName.trim() : appName;
|
||||
install();
|
||||
return currentAppName;
|
||||
}
|
||||
|
||||
return { install, applyVisibility, setVisible, setAppName };
|
||||
}
|
||||
Reference in New Issue
Block a user