Files
capcut-mate/desktop-client/main.js
T

152 lines
4.9 KiB
JavaScript

const { app, BrowserWindow, dialog, shell } = require('electron');
const path = require('path');
const logger = require('./nodeapi/logger');
// 引入IPC处理程序模块
const { setupIpcHandlers } = require('./nodeapi/ipcHandlers');
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) => {
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();
}
}
});