Files
capcut-mate/desktop-client/main.js
2026-08-24 22:42:08 +08:00

159 lines
5.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const { app, BrowserWindow, dialog, shell } = require('electron');
const path = require('path');
const logger = require('./nodeapi/logger');
// 引入IPC处理程序模块
const { setupIpcHandlers } = require('./nodeapi/ipcHandlers');
const { attachEditContextMenu } = require('./nodeapi/editContextMenu');
// Windows 任务栏 / 快捷方式图标依赖 AppUserModelId需与 electron-builder appId 一致
if (process.platform === 'win32') {
app.setAppUserModelId('com.gogoshine.capcut-mate');
}
let mainWindow;
let ipcHandlersInitialized = false;
function createWindow() {
// 避免重复创建窗口
if (mainWindow) {
mainWindow.show();
return mainWindow;
}
// 根据平台选择合适的图标格式
let iconPath;
if (process.platform === 'darwin') {
iconPath = path.join(__dirname, './assets/icons/logo.icns');
} else if (process.platform === 'win32') {
iconPath = path.join(__dirname, './assets/icons/logo.ico');
} else {
iconPath = path.join(__dirname, './assets/icons/logo.png');
}
mainWindow = new BrowserWindow({
width: 1366,
height: 868,
icon: iconPath,
show: false, // 创建窗口但先隐藏,等页面加载完成后再显示
webPreferences: {
nodeIntegration: false, // 禁用 Node.js 集成(出于安全考虑,强烈推荐)
contextIsolation: true, // 启用上下文隔离Electron 12 后默认 true推荐开启
preload: path.join(__dirname, 'preload.js'), // 指定预加载脚本的绝对路径
// webSecurity: false, // 禁用web安全策略可选根据需求调整
disableBlinkFeatures: 'OutOfBlinkCors', // 禁用某些Blink特性以提高性能
hardwareAcceleration: true // 启用硬件加速
}
});
// 在 macOS 上设置 Dock 图标
if (process.platform === 'darwin') {
app.dock.setIcon(path.join(__dirname, './assets/icons/logo.png'));
}
// 判断是否为开发模式
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged;
// 加载React应用
if (isDev) {
// 开发模式下加载本地web服务
mainWindow.loadURL('http://localhost:9000');
// 开发模式下打开开发者工具
mainWindow.webContents.openDevTools();
} else {
// 生产模式下加载构建后的文件
mainWindow.loadFile(path.join(__dirname, 'ui', 'index.html'));
}
// 当页面加载完成后显示窗口
mainWindow.webContents.on('ready-to-show', () => {
mainWindow.show();
});
// 只初始化一次IPC处理程序
if (!ipcHandlersInitialized) {
setupIpcHandlers(mainWindow);
ipcHandlersInitialized = true;
}
mainWindow.on('closed', function () {
mainWindow = null;
});
return mainWindow;
}
// 处理未捕获的异常,特别是与文件权限相关的错误
process.on('uncaughtException', (error) => {
logger.error('未捕获的异常:', error);
// 在 macOS 沙箱环境下,某些权限错误可能在这里捕获
if (process.platform === 'darwin' && error.message &&
(error.message.includes('Operation not permitted') || error.message.includes('EPERM'))) {
dialog.showMessageBoxSync({
type: 'error',
title: '权限错误',
message: '应用缺少必要的文件访问权限,请检查系统偏好设置中的隐私与安全性设置。',
detail: '请在 系统偏好设置 > 安全性与隐私 > 隐私 > 文件夹访问 中允许本应用访问相关文件夹。',
buttons: ['确定']
});
}
});
// 判断是否为需要外跳系统浏览器的外部链接
function isExternalUrl(url) {
return url && (url.startsWith('http://') || url.startsWith('https://'));
}
// 全局拦截所有 webContents 的新窗口打开行为
app.on('web-contents-created', (event, contents) => {
attachEditContextMenu(contents);
contents.setWindowOpenHandler(({ url }) => {
if (isExternalUrl(url)) {
shell.openExternal(url);
return { action: 'deny' };
}
return { action: 'allow' };
});
});
// 兜底:任何新窗口被创建时直接销毁并甩到系统浏览器
app.on('browser-window-created', (event, window) => {
// 用 getAllWindows 判断:如果当前只有这一个窗口,说明是主窗口,放过
if (BrowserWindow.getAllWindows().length === 1) return;
// 检测不是外部链接则放过
const url = window.webContents.getURL();
if (!isExternalUrl(url)) return;
window.webContents.once('did-finish-load', () => {
shell.openExternal(url);
window.destroy();
});
});
// 当Electron完成初始化并准备创建浏览器窗口时调用此方法
app.whenReady().then(() => {
createWindow();
});
// 当所有窗口都关闭时退出应用
app.on('window-all-closed', () => {
// 在macOS上应用及其菜单栏通常保持活动状态直到用户使用Cmd + Q明确退出
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// 在macOS上当单击dock图标时如果没有任何窗口打开则创建一个
// 否则显示已存在的窗口
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
} else {
// 如果已有窗口,确保它被显示和聚焦
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
}
});