feat: 脚本录制功能完善

This commit is contained in:
duanshuwen
2026-04-12 22:12:57 +08:00
parent 6432634d17
commit 66db6c462e
16 changed files with 262 additions and 124 deletions

View File

@@ -11,12 +11,9 @@ import {
toggleScript,
} from '@electron/service/script-store-service';
import { runScriptById } from '@electron/service/script-execution-service';
import {
startRecording,
stopRecording,
} from '@electron/service/script-recorder-service';
import fs from 'fs'
import path from 'path'
import { spawn } from 'child_process'
import log from 'electron-log';
const openedTabIndexByChannelName = new Map<string, number>()
@@ -29,6 +26,9 @@ function getScriptsDir() {
export function runTaskOperationService() {
const executeScriptServiceInstance = new executeScriptService();
const playwrightCoreDir = path.dirname(require.resolve('playwright-core'));
const cliPath = path.join(playwrightCoreDir, 'cli.js');
let recorderProc: ReturnType<typeof spawn> | null = null;
// 脚本管理 IPC
ipcMain.handle(IPC_EVENTS.SCRIPT_LIST, async () => {
@@ -88,7 +88,35 @@ export function runTaskOperationService() {
ipcMain.handle(IPC_EVENTS.SCRIPT_RECORD_START, async (_event, url?: string) => {
try {
return await startRecording(url);
if (recorderProc) {
recorderProc.kill('SIGINT');
recorderProc = null;
}
const targetUrl = url || 'about:blank';
recorderProc = spawn(process.execPath, [cliPath, 'codegen', '--target', 'javascript', '--channel', 'chrome', '--viewport-size', '1920,1080', '--color-scheme', 'light', targetUrl], {
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },
stdio: 'pipe',
});
recorderProc.on('error', (err) => {
log.error('[SCRIPT_RECORD_START] Failed to start codegen process:', err);
});
recorderProc.on('exit', (code, signal) => {
log.info(`[SCRIPT_RECORD_START] Process exited code=${code} signal=${signal}`);
recorderProc = null;
});
recorderProc.stdout?.on('data', (data: Buffer) => {
log.info(`[SCRIPT_RECORD_START] stdout: ${data.toString()}`);
});
recorderProc.stderr?.on('data', (data: Buffer) => {
log.error(`[SCRIPT_RECORD_START] stderr: ${data.toString()}`);
});
return { success: true };
} catch (error: any) {
log.error('[SCRIPT_RECORD_START] error:', error);
return { success: false, error: error?.message || 'Recording start failed' };
@@ -97,13 +125,76 @@ export function runTaskOperationService() {
ipcMain.handle(IPC_EVENTS.SCRIPT_RECORD_STOP, async () => {
try {
return await stopRecording();
if (recorderProc) {
recorderProc.kill('SIGINT');
recorderProc = null;
}
return { success: true, code: '' };
} catch (error: any) {
log.error('[SCRIPT_RECORD_STOP] error:', error);
return { success: false, error: error?.message || 'Recording stop failed' };
}
});
ipcMain.handle(IPC_EVENTS.SCRIPT_CODEGEN, async (_event, id: string, url?: string) => {
try {
const script = getScript(id);
if (!script) {
return { success: false, error: 'Script not found' };
}
const scriptsDir = getScriptsDir();
const scriptPath = path.join(scriptsDir, script.filename);
const targetUrl = url || 'about:blank';
log.info(`[SCRIPT_CODEGEN] Starting codegen for script ${id} at ${scriptPath} with url ${targetUrl}`);
return await new Promise<{ success: boolean; code?: string; error?: string }>((resolve) => {
const proc = spawn(process.execPath, [cliPath, 'codegen', '--target', 'javascript', '--channel', 'chrome', '-o', scriptPath, targetUrl], {
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },
stdio: 'pipe',
});
proc.on('exit', () => {
try {
let generatedCode = fs.readFileSync(scriptPath, 'utf-8');
// Playwright codegen --target javascript generates CommonJS code.
// Since script files use .mjs extension, we inject createRequire for compatibility.
if (generatedCode.includes("require('playwright')") && !generatedCode.includes('createRequire')) {
generatedCode = `import { createRequire } from 'node:module';\nconst require = createRequire(import.meta.url);\n\n${generatedCode}`;
}
fs.writeFileSync(scriptPath, generatedCode, 'utf-8');
// Update script store so the new code is reflected in metadata reads
saveScript({
id,
name: script.name,
description: script.description,
code: generatedCode,
channel: script.channel,
enabled: script.enabled,
});
resolve({ success: true, code: generatedCode });
} catch (err: any) {
log.error('[SCRIPT_CODEGEN] Failed to process generated code:', err);
resolve({ success: false, error: err?.message || 'Failed to process generated code' });
}
});
proc.on('error', (err) => {
log.error('[SCRIPT_CODEGEN] Failed to start codegen:', err);
resolve({ success: false, error: err.message });
});
});
} catch (error: any) {
log.error('[SCRIPT_CODEGEN] error:', error);
return { success: false, error: error?.message || 'Codegen failed' };
}
});
// 打开渠道
ipcMain.handle(IPC_EVENTS.OPEN_CHANNEL, async (_event, channels: any) => {
try {