Files
ChatLab/electron/main/index.ts
2025-12-28 16:43:47 +08:00

210 lines
5.5 KiB
TypeScript
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.
import { app, shell, BrowserWindow, protocol, nativeTheme } from 'electron'
import { join } from 'path'
import { optimizer, is, platform } from '@electron-toolkit/utils'
import * as fs from 'fs/promises'
import { checkUpdate } from './update'
import mainIpcMain from './ipcMain'
import { initAnalytics, trackDailyActive } from './analytics'
import { initProxy } from './network/proxy'
class MainProcess {
mainWindow: BrowserWindow | null
constructor() {
// 主窗口
this.mainWindow = null
// 设置应用程序名称
if (process.platform === 'win32') app.setAppUserModelId(app.getName())
// 初始化
this.checkApp().then(async (lockObtained) => {
if (lockObtained) {
await this.init()
}
})
}
// 单例锁
async checkApp() {
if (!app.requestSingleInstanceLock()) {
app.quit()
// 未获得锁
return false
}
// 聚焦到当前程序
else {
app.on('second-instance', () => {
if (this.mainWindow) {
this.mainWindow.show()
if (this.mainWindow.isMinimized()) this.mainWindow.restore()
this.mainWindow.focus()
}
})
// 获得锁
return true
}
}
// 初始化程序
async init() {
initAnalytics()
initProxy() // 初始化代理配置
// 注册应用协议
app.setAsDefaultProtocolClient('chatlab')
// 应用程序准备好之前注册
protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])
// 主应用程序事件
this.mainAppEvents()
}
// 创建主窗口
async createWindow() {
this.mainWindow = new BrowserWindow({
width: 1180,
height: 752,
minWidth: 1180,
minHeight: 752,
show: false,
autoHideMenuBar: true,
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
devTools: true,
},
})
// 设置默认日间模式
nativeTheme.themeSource = 'light'
this.mainWindow.once('ready-to-show', () => {
this.mainWindow?.show()
})
// 主窗口事件
this.mainWindowEvents()
this.mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
})
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
this.mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else {
this.mainWindow.loadFile(join(__dirname, '../../out/renderer/index.html'))
}
}
// 主应用程序事件
mainAppEvents() {
app.whenReady().then(async () => {
console.log('[Main] App is ready')
// 设置Windows应用程序用户模型id
if (process.platform === 'win32') app.setAppUserModelId(app.getName())
// 记录日活(用于统计操作系统版本、客户端版本,便于更好的适配客户端)
trackDailyActive()
// 创建主窗口
console.log('[Main] Creating window...')
await this.createWindow()
console.log('[Main] Window created')
// 检查更新逻辑
checkUpdate(this.mainWindow)
// 引入主进程ipcMain
if (this.mainWindow) {
console.log('[Main] Registering IPC handlers...')
mainIpcMain(this.mainWindow)
console.log('[Main] IPC handlers registered')
}
// 开发环境下 F12 打开控制台
app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window)
})
app.on('activate', () => {
// 在 macOS 上,当单击 Dock 图标且没有其他窗口时,通常会重新创建窗口
if (BrowserWindow.getAllWindows().length === 0) {
this.createWindow()
return
}
if (platform.isMacOS) {
this.mainWindow?.show()
}
})
// 监听渲染进程崩溃
app.on('render-process-gone', (e, w, d) => {
if (d.reason == 'crashed') {
w.reload()
}
// fs.appendFile(`./error-log-${+new Date()}.txt`, `${new Date()}渲染进程被杀死${d.reason}\n`)
})
// 自定义协议
app.on('open-url', (_, url) => {
console.log('Received custom protocol URL:', url)
})
// 当所有窗口都关闭时退出应用macOS 除外
app.on('window-all-closed', () => {
if (!platform.isMacOS) {
app.quit()
}
})
// 只有显式调用quit才退出系统区分MAC系统程序坞退出和点击X隐藏
app.on('before-quit', () => {
// @ts-ignore
app.isQuiting = true
})
})
}
// 主窗口事件
mainWindowEvents() {
if (!this.mainWindow) {
return
}
this.mainWindow.webContents.on('did-finish-load', () => {
setTimeout(() => {
this.mainWindow && this.mainWindow.webContents.send('app-started')
}, 500)
})
this.mainWindow.on('maximize', () => {
this.mainWindow?.webContents.send('windowState', true)
})
this.mainWindow.on('unmaximize', () => {
this.mainWindow?.webContents.send('windowState', false)
})
// 窗口关闭
this.mainWindow.on('close', (event) => {
event.preventDefault()
// @ts-ignore
if (!app.isQuiting) {
this.mainWindow?.hide()
} else {
app.exit()
}
})
}
}
// 捕获未捕获的异常
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error)
})
new MainProcess()