feat: 脚本录制功能完善
This commit is contained in:
@@ -1,4 +1,26 @@
|
||||
"use strict";
|
||||
var __create = Object.create;
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __getProtoOf = Object.getPrototypeOf;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||
// If the importer is in node compatibility mode or this is not an ESM
|
||||
// file that has been converted to a CommonJS file using a Babel-
|
||||
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||
mod
|
||||
));
|
||||
const electron = require("electron");
|
||||
const OpenAI = require("openai");
|
||||
const util = require("util");
|
||||
@@ -13,7 +35,6 @@ const net = require("net");
|
||||
const http = require("http");
|
||||
const child_process = require("child_process");
|
||||
const events = require("events");
|
||||
const playwright = require("playwright");
|
||||
require("bytenode");
|
||||
const electronUpdater = require("electron-updater");
|
||||
function _interopNamespaceDefault(e) {
|
||||
@@ -88,6 +109,7 @@ var IPC_EVENTS = /* @__PURE__ */ ((IPC_EVENTS2) => {
|
||||
IPC_EVENTS2["SCRIPT_RUN"] = "script:run";
|
||||
IPC_EVENTS2["SCRIPT_RECORD_START"] = "script:record-start";
|
||||
IPC_EVENTS2["SCRIPT_RECORD_STOP"] = "script:record-stop";
|
||||
IPC_EVENTS2["SCRIPT_CODEGEN"] = "script:codegen";
|
||||
IPC_EVENTS2["UPDATE_CHECK"] = "update:check";
|
||||
IPC_EVENTS2["UPDATE_DOWNLOAD"] = "update:download";
|
||||
IPC_EVENTS2["UPDATE_INSTALL"] = "update:install";
|
||||
@@ -793,7 +815,7 @@ class WindowService {
|
||||
}
|
||||
_loadPage(window2, pageName) {
|
||||
{
|
||||
return window2.loadURL(`${"http://localhost:5173"}/${pageName}.html`);
|
||||
return window2.loadURL(`${"http://localhost:5173/"}/${pageName}.html`);
|
||||
}
|
||||
}
|
||||
_loadWindowTemplate(window2, name) {
|
||||
@@ -1702,59 +1724,15 @@ async function runScriptById(id, channel) {
|
||||
});
|
||||
return result;
|
||||
}
|
||||
let recorderBrowser = null;
|
||||
let recorderContext = null;
|
||||
async function startRecording(url) {
|
||||
try {
|
||||
await launchLocalChrome();
|
||||
if (recorderBrowser) {
|
||||
await stopRecording();
|
||||
}
|
||||
recorderBrowser = await playwright.chromium.connectOverCDP("http://127.0.0.1:9222");
|
||||
recorderContext = recorderBrowser.contexts()[0] || await recorderBrowser.newContext();
|
||||
const page = await recorderContext.newPage();
|
||||
const targetUrl = url || "about:blank";
|
||||
await page.goto(targetUrl, { waitUntil: "domcontentloaded" });
|
||||
await page.pause();
|
||||
return {
|
||||
success: true,
|
||||
code: ""
|
||||
};
|
||||
} catch (error) {
|
||||
log.error("[script-recorder-service] Failed to start recording:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error?.message || "Failed to start recording"
|
||||
};
|
||||
}
|
||||
}
|
||||
async function stopRecording() {
|
||||
try {
|
||||
if (recorderContext) {
|
||||
await recorderContext.close().catch(() => {
|
||||
});
|
||||
recorderContext = null;
|
||||
}
|
||||
if (recorderBrowser) {
|
||||
await recorderBrowser.close().catch(() => {
|
||||
});
|
||||
recorderBrowser = null;
|
||||
}
|
||||
return { success: true, code: "" };
|
||||
} catch (error) {
|
||||
log.error("[script-recorder-service] Failed to stop recording:", error);
|
||||
return {
|
||||
success: false,
|
||||
error: error?.message || "Failed to stop recording"
|
||||
};
|
||||
}
|
||||
}
|
||||
const openedTabIndexByChannelName = /* @__PURE__ */ new Map();
|
||||
function getScriptsDir() {
|
||||
return electron.app.isPackaged ? path.join(__dirname, "scripts") : path.join(process.cwd(), "electron/scripts");
|
||||
}
|
||||
function runTaskOperationService() {
|
||||
const executeScriptServiceInstance = new executeScriptService();
|
||||
const playwrightCoreDir = path.dirname(require.resolve("playwright-core"));
|
||||
const cliPath = path.join(playwrightCoreDir, "cli.js");
|
||||
let recorderProc = null;
|
||||
electron.ipcMain.handle(IPC_EVENTS.SCRIPT_LIST, async () => {
|
||||
try {
|
||||
return listScripts();
|
||||
@@ -1806,7 +1784,29 @@ function runTaskOperationService() {
|
||||
});
|
||||
electron.ipcMain.handle(IPC_EVENTS.SCRIPT_RECORD_START, async (_event, url) => {
|
||||
try {
|
||||
return await startRecording(url);
|
||||
if (recorderProc) {
|
||||
recorderProc.kill("SIGINT");
|
||||
recorderProc = null;
|
||||
}
|
||||
const targetUrl = url || "about:blank";
|
||||
recorderProc = child_process.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) => {
|
||||
log.info(`[SCRIPT_RECORD_START] stdout: ${data.toString()}`);
|
||||
});
|
||||
recorderProc.stderr?.on("data", (data) => {
|
||||
log.error(`[SCRIPT_RECORD_START] stderr: ${data.toString()}`);
|
||||
});
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
log.error("[SCRIPT_RECORD_START] error:", error);
|
||||
return { success: false, error: error?.message || "Recording start failed" };
|
||||
@@ -1814,12 +1814,65 @@ function runTaskOperationService() {
|
||||
});
|
||||
electron.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) {
|
||||
log.error("[SCRIPT_RECORD_STOP] error:", error);
|
||||
return { success: false, error: error?.message || "Recording stop failed" };
|
||||
}
|
||||
});
|
||||
electron.ipcMain.handle(IPC_EVENTS.SCRIPT_CODEGEN, async (_event, id, url) => {
|
||||
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((resolve) => {
|
||||
const proc = child_process.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");
|
||||
if (generatedCode.includes("require('playwright')") && !generatedCode.includes("createRequire")) {
|
||||
generatedCode = `import { createRequire } from 'node:module';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
${generatedCode}`;
|
||||
}
|
||||
fs.writeFileSync(scriptPath, generatedCode, "utf-8");
|
||||
saveScript({
|
||||
id,
|
||||
name: script.name,
|
||||
description: script.description,
|
||||
code: generatedCode,
|
||||
channel: script.channel,
|
||||
enabled: script.enabled
|
||||
});
|
||||
resolve({ success: true, code: generatedCode });
|
||||
} catch (err) {
|
||||
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) {
|
||||
log.error("[SCRIPT_CODEGEN] error:", error);
|
||||
return { success: false, error: error?.message || "Codegen failed" };
|
||||
}
|
||||
});
|
||||
electron.ipcMain.handle(IPC_EVENTS.OPEN_CHANNEL, async (_event, channels) => {
|
||||
try {
|
||||
await launchLocalChrome();
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -54,6 +54,7 @@ var IPC_EVENTS = /* @__PURE__ */ ((IPC_EVENTS2) => {
|
||||
IPC_EVENTS2["SCRIPT_RUN"] = "script:run";
|
||||
IPC_EVENTS2["SCRIPT_RECORD_START"] = "script:record-start";
|
||||
IPC_EVENTS2["SCRIPT_RECORD_STOP"] = "script:record-stop";
|
||||
IPC_EVENTS2["SCRIPT_CODEGEN"] = "script:codegen";
|
||||
IPC_EVENTS2["UPDATE_CHECK"] = "update:check";
|
||||
IPC_EVENTS2["UPDATE_DOWNLOAD"] = "update:download";
|
||||
IPC_EVENTS2["UPDATE_INSTALL"] = "update:install";
|
||||
@@ -112,7 +113,8 @@ const api = {
|
||||
toggle: (id, enabled) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_TOGGLE, id, enabled),
|
||||
run: (id) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RUN, id),
|
||||
startRecording: (url) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RECORD_START, url),
|
||||
stopRecording: () => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RECORD_STOP)
|
||||
stopRecording: () => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RECORD_STOP),
|
||||
codegen: (id, url) => electron.ipcRenderer.invoke(IPC_EVENTS.SCRIPT_CODEGEN, id, url)
|
||||
}
|
||||
};
|
||||
electron.contextBridge.exposeInMainWorld("api", api);
|
||||
|
||||
4
dist/index.html
vendored
4
dist/index.html
vendored
@@ -8,8 +8,8 @@
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' http://8.138.234.141 https://one-feel-bucket.oss-cn-guangzhou.aliyuncs.com; connect-src 'self' http://8.138.234.141 https://api.iconify.design wss://onefeel.brother7.cn"
|
||||
/>
|
||||
<script type="module" crossorigin src="./assets/index-CIC9LE5u.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-Dck3WKYD.css">
|
||||
<script type="module" crossorigin src="./assets/index-CBOhT_7U.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-Dov57n46.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -66,6 +66,7 @@ const api: WindowApi = {
|
||||
run: (id: string) => ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RUN, id),
|
||||
startRecording: (url?: string) => ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RECORD_START, url),
|
||||
stopRecording: () => ipcRenderer.invoke(IPC_EVENTS.SCRIPT_RECORD_STOP),
|
||||
codegen: (id: string, url?: string) => ipcRenderer.invoke(IPC_EVENTS.SCRIPT_CODEGEN, id, url),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -28,7 +28,12 @@
|
||||
"enabled": true,
|
||||
"channel": "fliggy",
|
||||
"createdAt": "2026-04-09T19:35:34.000Z",
|
||||
"updatedAt": "2026-04-09T19:35:34.000Z"
|
||||
"updatedAt": "2026-04-12T12:59:12.117Z",
|
||||
"lastRun": {
|
||||
"time": "2026-04-12T12:59:12.117Z",
|
||||
"success": false,
|
||||
"error": "Script exited with code 1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "script-mt-trace",
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import { chromium } from 'playwright';
|
||||
import log from 'electron-log';
|
||||
import { launchLocalChrome } from '@electron/utils/chrome/launchLocalChrome';
|
||||
|
||||
let recorderBrowser: any = null;
|
||||
let recorderContext: any = null;
|
||||
|
||||
export async function startRecording(url?: string): Promise<{ success: boolean; code?: string; error?: string }> {
|
||||
try {
|
||||
await launchLocalChrome();
|
||||
|
||||
if (recorderBrowser) {
|
||||
await stopRecording();
|
||||
}
|
||||
|
||||
recorderBrowser = await chromium.connectOverCDP('http://127.0.0.1:9222');
|
||||
recorderContext = recorderBrowser.contexts()[0] || (await recorderBrowser.newContext());
|
||||
const page = await recorderContext.newPage();
|
||||
const targetUrl = url || 'about:blank';
|
||||
await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// 唤起 Playwright Inspector,让用户手动录制并生成代码
|
||||
await page.pause();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
code: '',
|
||||
};
|
||||
} catch (error: any) {
|
||||
log.error('[script-recorder-service] Failed to start recording:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error?.message || 'Failed to start recording',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function stopRecording(): Promise<{ success: boolean; code?: string; error?: string }> {
|
||||
try {
|
||||
if (recorderContext) {
|
||||
await recorderContext.close().catch(() => {});
|
||||
recorderContext = null;
|
||||
}
|
||||
if (recorderBrowser) {
|
||||
await recorderBrowser.close().catch(() => {});
|
||||
recorderBrowser = null;
|
||||
}
|
||||
return { success: true, code: '' };
|
||||
} catch (error: any) {
|
||||
log.error('[script-recorder-service] Failed to stop recording:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error?.message || 'Failed to stop recording',
|
||||
};
|
||||
}
|
||||
}
|
||||
5
global.d.ts
vendored
5
global.d.ts
vendored
@@ -88,6 +88,10 @@ declare global {
|
||||
params: []
|
||||
return: Promise<{ success: boolean; code?: string; error?: string }>
|
||||
}
|
||||
[IPC_EVENTS.SCRIPT_CODEGEN]: {
|
||||
params: [id: string, url?: string]
|
||||
return: Promise<{ success: boolean; code?: string; error?: string }>
|
||||
}
|
||||
}
|
||||
|
||||
type TabId = string
|
||||
@@ -142,6 +146,7 @@ declare global {
|
||||
run: (id: string) => Promise<ScriptExecutionResult>,
|
||||
startRecording: (url?: string) => Promise<{ success: boolean; code?: string; error?: string }>,
|
||||
stopRecording: () => Promise<{ success: boolean; code?: string; error?: string }>,
|
||||
codegen: (id: string, url?: string) => Promise<{ success: boolean; code?: string; error?: string }>,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ export enum IPC_EVENTS {
|
||||
SCRIPT_RUN = 'script:run',
|
||||
SCRIPT_RECORD_START = 'script:record-start',
|
||||
SCRIPT_RECORD_STOP = 'script:record-stop',
|
||||
SCRIPT_CODEGEN = 'script:codegen',
|
||||
|
||||
// 更新
|
||||
UPDATE_CHECK = 'update:check',
|
||||
|
||||
@@ -15,4 +15,6 @@ export const scriptApi = {
|
||||
window.api.scriptApi.startRecording(url),
|
||||
stopRecording: (): Promise<{ success: boolean; code?: string; error?: string }> =>
|
||||
window.api.scriptApi.stopRecording(),
|
||||
codegen: (id: string, url?: string): Promise<{ success: boolean; code?: string; error?: string }> =>
|
||||
window.api.scriptApi.codegen(id, url),
|
||||
};
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
:loading="saving"
|
||||
class="!rounded-full !px-6 !h-[42px] !text-[13px] !font-semibold"
|
||||
>
|
||||
{{ saving ? t('common.saving', 'Saving...') : t('script.dialog.createTitle') }}
|
||||
{{ saving ? t('common.saving', 'Saving...') : t('script.dialog.createAndRecord', '创建并录制') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,7 +124,6 @@ const form = ref({
|
||||
name: '',
|
||||
description: '',
|
||||
channel: '',
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
function resetForm() {
|
||||
@@ -132,7 +131,6 @@ function resetForm() {
|
||||
name: '',
|
||||
description: '',
|
||||
channel: '',
|
||||
enabled: true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -149,7 +147,7 @@ function handleSubmit() {
|
||||
description: form.value.description.trim(),
|
||||
code: defaultScriptTemplate,
|
||||
channel: form.value.channel,
|
||||
enabled: form.value.enabled,
|
||||
enabled: true,
|
||||
};
|
||||
emit('save', payload);
|
||||
visible.value = false;
|
||||
|
||||
@@ -193,7 +193,7 @@ function openEditDialog(script: AutomationScript) {
|
||||
}
|
||||
|
||||
function handleCreateDialogClose() {
|
||||
editingScript.value = undefined;
|
||||
// editingScript lifecycle is handled by handleSave / handleEditDialogClose
|
||||
}
|
||||
|
||||
function handleEditDialogClose() {
|
||||
@@ -207,8 +207,17 @@ async function handleSave(input: ScriptSaveInput) {
|
||||
await store.saveScript(input);
|
||||
ElMessage.success(t('script.toast.updated'));
|
||||
} else {
|
||||
await store.saveScript(input);
|
||||
const result = await store.saveScript(input);
|
||||
ElMessage.success(t('script.toast.created'));
|
||||
ElMessage.info(t('script.toast.codegenStarting', '正在启动 Playwright codegen 录制,请在新窗口中操作,完成后关闭窗口即可自动保存代码'));
|
||||
const codegenResult = await store.codegen(result.id, input.channel || 'about:blank');
|
||||
if (codegenResult.success) {
|
||||
ElMessage.success(t('script.toast.codegenFinished', '录制完成,代码已保存'));
|
||||
editingScript.value = { ...result, code: codegenResult.code || '' };
|
||||
editDialogVisible.value = true;
|
||||
} else {
|
||||
ElMessage.error(codegenResult.error || t('script.toast.codegenFailed', '录制失败'));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
|
||||
@@ -140,6 +140,20 @@ export const useScriptStore = defineStore('script', () => {
|
||||
recordingStatus.value = 'idle';
|
||||
};
|
||||
|
||||
const codegen = async (id: string, url?: string) => {
|
||||
try {
|
||||
const result = await scriptApi.codegen(id, url);
|
||||
if (!result.success) {
|
||||
error.value = result.error || 'Codegen failed';
|
||||
}
|
||||
return result;
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
error.value = msg;
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
scripts,
|
||||
loading,
|
||||
@@ -157,5 +171,6 @@ export const useScriptStore = defineStore('script', () => {
|
||||
startRecording,
|
||||
stopRecording,
|
||||
resetRecording,
|
||||
codegen,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -72,6 +72,9 @@ export default defineConfig(({ mode, command }) => {
|
||||
rollupOptions: {
|
||||
external: isMainProcessExternal,
|
||||
},
|
||||
watch: {
|
||||
exclude: ['**/electron/scripts/**', '**/scripts.meta.json'],
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
@@ -100,6 +103,9 @@ export default defineConfig(({ mode, command }) => {
|
||||
entryFileNames: 'preload.js',
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
exclude: ['**/electron/scripts/**', '**/scripts.meta.json'],
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
@@ -139,6 +145,9 @@ export default defineConfig(({ mode, command }) => {
|
||||
|
||||
server: {
|
||||
port: 5173,
|
||||
watch: {
|
||||
ignored: ['**/electron/scripts/**', '**/scripts.meta.json'],
|
||||
},
|
||||
proxy: {
|
||||
'/ingress': {
|
||||
target: 'http://8.138.234.141',
|
||||
|
||||
Reference in New Issue
Block a user