feat: integrate Playwright download dialog into startup flow

This commit is contained in:
Misaka_Company
2026-03-24 16:02:31 +08:00
parent a711781f21
commit fbd62fe390
8 changed files with 101 additions and 16 deletions

View File

@@ -31,7 +31,11 @@ export function setupElectronRuntime(): void {
})
}
export function ensurePlaywrightRuntime(browsersPath: string): void {
/**
* Check if Playwright browsers are installed
* @returns true if browsers exist, false otherwise
*/
export function ensurePlaywrightRuntime(browsersPath: string): boolean {
try {
fs.mkdirSync(browsersPath, { recursive: true })
} catch (error) {
@@ -43,7 +47,7 @@ export function ensurePlaywrightRuntime(browsersPath: string): void {
const chromiumPath = fs.existsSync(newChromiumPath) ? newChromiumPath : oldChromiumPath
if (fs.existsSync(chromiumPath)) {
return
return true
}
let foundRevision = false
@@ -64,22 +68,14 @@ export function ensurePlaywrightRuntime(browsersPath: string): void {
}
if (foundRevision) {
return
return true
}
dialog.showErrorBox(
'浏览器文件未找到',
`Playwright 浏览器文件不存在。\n\n` +
`期望路径:${newChromiumPath}\n` +
`或:${oldChromiumPath}\n\n` +
`当前目录内容:${fs.existsSync(browsersPath) ? fs.readdirSync(browsersPath).join(', ') : '目录不存在'}\n\n` +
`请运行以下命令安装浏览器:\n` +
`npx playwright install chromium`
)
console.warn(
'Playwright browser not found. Available:',
fs.existsSync(browsersPath) ? fs.readdirSync(browsersPath) : 'none'
)
return false
}
export async function initializeMainProcessServices(): Promise<void> {

View File

@@ -12,7 +12,8 @@ app.whenReady().then(async () => {
setupProcessGuards()
registerMainWindowLifecycle()
const playwrightBrowsersPath = configurePlaywrightBrowsersPath()
ensurePlaywrightRuntime(playwrightBrowsersPath)
const browsersExist = ensurePlaywrightRuntime(playwrightBrowsersPath)
console.log('Playwright browsers exist:', browsersExist)
await initializeMainProcessServices()
setupElectronRuntime()

View File

@@ -3,7 +3,8 @@
* Handles browser download requests from renderer process
*/
import { ipcMain, IpcMainInvokeEvent } from 'electron'
import { app, ipcMain, IpcMainInvokeEvent } from 'electron'
import { join } from 'path'
import { IPC_CHANNELS } from '../../shared/ipc-channels'
import { withErrorHandling, type IpcResult } from './index'
import { DownloadService } from '../services/playwright-browser'
@@ -31,12 +32,51 @@ function createS3Client(): S3Client {
})
}
/**
* Check if Playwright browsers are installed
*/
async function checkBrowsersExist(): Promise<boolean> {
const fs = await import('fs')
const browsersPath = join(app.getPath('userData'), 'ms-playwright')
const newChromiumPath = join(browsersPath, 'chromium-1208', 'chrome-win64', 'chrome.exe')
const oldChromiumPath = join(browsersPath, 'chromium-win32', 'chrome.exe')
const chromiumPath = fs.default.existsSync(newChromiumPath) ? newChromiumPath : oldChromiumPath
if (fs.default.existsSync(chromiumPath)) {
return true
}
let foundRevision = false
try {
const entries = fs.default.readdirSync(browsersPath)
for (const entry of entries) {
if (entry.startsWith('chromium-') && !entry.includes('headless')) {
const revisionPath = join(browsersPath, entry, 'chrome-win64', 'chrome.exe')
if (fs.default.existsSync(revisionPath)) {
foundRevision = true
break
}
}
}
} catch {
// Ignore browser directory probing failures
}
return foundRevision
}
/**
* Track active download for cancellation
*/
let activeDownload: { service: DownloadService; cancelled: boolean } | null = null
export function registerPlaywrightBrowserHandlers(): void {
ipcMain.handle(IPC_CHANNELS.PLAYWRIGHT_BROWSER_CHECK, async (): Promise<IpcResult<boolean>> => {
return withErrorHandling(async () => {
return checkBrowsersExist()
}, 'playwright-browser:check')
})
ipcMain.handle(
IPC_CHANNELS.PLAYWRIGHT_BROWSER_DOWNLOAD,
async (event: IpcMainInvokeEvent): Promise<IpcResult<void>> => {