@dimina-kit/devtools 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main/index.bundle.js +2765 -0
- package/dist/main/services/default-adapter.js +1 -3
- package/dist/main/services/projects/project-repository.js +6 -0
- package/dist/main/services/simulator/dir.d.ts +1 -1
- package/dist/main/services/simulator/dir.js +1 -4
- package/dist/main/services/simulator-storage/index.js +40 -1
- package/dist/main/services/workspace/workspace-service.js +12 -0
- package/dist/main/utils/paths.d.ts +2 -0
- package/dist/main/utils/paths.js +27 -4
- package/dist/preload/instrumentation/wxml-extract.js +134 -3
- package/dist/preload/runtime/bridge.d.ts +3 -1
- package/dist/preload/runtime/bridge.js +55 -4
- package/dist/preload/shared/types.d.ts +2 -1
- package/dist/preload/windows/simulator.js +100 -7
- package/dist/renderer/assets/index-D1p6crtf.js +46 -0
- package/dist/renderer/assets/{input-VLotwer2.js → input-DxfYnzX_.js} +2 -2
- package/dist/renderer/assets/{ipc-transport-COKIC6Ny.js → ipc-transport-ClIvDcYY.js} +6 -6
- package/dist/renderer/assets/ipc-transport-EiWxu_hG.css +1 -0
- package/dist/renderer/assets/{popover-Dn0RwMLI.js → popover-CtXcBJtM.js} +2 -2
- package/dist/renderer/assets/{select-DzIsLaKX.js → select-DMfp49f1.js} +2 -2
- package/dist/renderer/assets/{settings-CD-dacWB.js → settings-DteGraEU.js} +2 -2
- package/dist/renderer/assets/{settings-api-BV9MPckG.js → settings-api-CY84UMpN.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-ClwgsvJL.js → workbenchSettings-DjELLnSJ.js} +2 -2
- package/dist/renderer/entries/main/index.html +5 -5
- package/dist/renderer/entries/popover/index.html +5 -5
- package/dist/renderer/entries/settings/index.html +5 -5
- package/dist/renderer/entries/workbench-settings/index.html +4 -4
- package/dist/shared/ipc-channels.d.ts +23 -0
- package/dist/shared/ipc-channels.js +5 -0
- package/package.json +21 -19
- package/dist/main/api.d.ts.map +0 -1
- package/dist/main/api.js.map +0 -1
- package/dist/main/app/app.d.ts.map +0 -1
- package/dist/main/app/app.js.map +0 -1
- package/dist/main/app/bootstrap.d.ts.map +0 -1
- package/dist/main/app/bootstrap.js.map +0 -1
- package/dist/main/app/launch.d.ts.map +0 -1
- package/dist/main/app/launch.js.map +0 -1
- package/dist/main/app/lifecycle.d.ts.map +0 -1
- package/dist/main/app/lifecycle.js.map +0 -1
- package/dist/main/index.d.ts.map +0 -1
- package/dist/main/index.js.map +0 -1
- package/dist/main/ipc/app.d.ts.map +0 -1
- package/dist/main/ipc/app.js.map +0 -1
- package/dist/main/ipc/index.d.ts.map +0 -1
- package/dist/main/ipc/index.js.map +0 -1
- package/dist/main/ipc/panels.d.ts.map +0 -1
- package/dist/main/ipc/panels.js.map +0 -1
- package/dist/main/ipc/popover.d.ts.map +0 -1
- package/dist/main/ipc/popover.js.map +0 -1
- package/dist/main/ipc/projects.d.ts.map +0 -1
- package/dist/main/ipc/projects.js.map +0 -1
- package/dist/main/ipc/session.d.ts.map +0 -1
- package/dist/main/ipc/session.js.map +0 -1
- package/dist/main/ipc/settings.d.ts.map +0 -1
- package/dist/main/ipc/settings.js.map +0 -1
- package/dist/main/ipc/simulator.d.ts.map +0 -1
- package/dist/main/ipc/simulator.js.map +0 -1
- package/dist/main/ipc/toolbar.d.ts.map +0 -1
- package/dist/main/ipc/toolbar.js.map +0 -1
- package/dist/main/menu/index.d.ts.map +0 -1
- package/dist/main/menu/index.js.map +0 -1
- package/dist/main/services/automation/exec.d.ts.map +0 -1
- package/dist/main/services/automation/exec.js.map +0 -1
- package/dist/main/services/automation/handlers/app.d.ts.map +0 -1
- package/dist/main/services/automation/handlers/app.js.map +0 -1
- package/dist/main/services/automation/handlers/element.d.ts.map +0 -1
- package/dist/main/services/automation/handlers/element.js.map +0 -1
- package/dist/main/services/automation/handlers/page.d.ts.map +0 -1
- package/dist/main/services/automation/handlers/page.js.map +0 -1
- package/dist/main/services/automation/handlers/tool.d.ts.map +0 -1
- package/dist/main/services/automation/handlers/tool.js.map +0 -1
- package/dist/main/services/automation/index.d.ts.map +0 -1
- package/dist/main/services/automation/index.js.map +0 -1
- package/dist/main/services/automation/registry.d.ts.map +0 -1
- package/dist/main/services/automation/registry.js.map +0 -1
- package/dist/main/services/automation/shared.d.ts.map +0 -1
- package/dist/main/services/automation/shared.js.map +0 -1
- package/dist/main/services/default-adapter.d.ts.map +0 -1
- package/dist/main/services/default-adapter.js.map +0 -1
- package/dist/main/services/layout/index.d.ts.map +0 -1
- package/dist/main/services/layout/index.js.map +0 -1
- package/dist/main/services/mcp/index.d.ts.map +0 -1
- package/dist/main/services/mcp/index.js.map +0 -1
- package/dist/main/services/mcp/server.d.ts.map +0 -1
- package/dist/main/services/mcp/server.js.map +0 -1
- package/dist/main/services/mcp/target-manager.d.ts.map +0 -1
- package/dist/main/services/mcp/target-manager.js.map +0 -1
- package/dist/main/services/mcp/tool-registry.d.ts.map +0 -1
- package/dist/main/services/mcp/tool-registry.js.map +0 -1
- package/dist/main/services/mcp/tools/context-tools.d.ts.map +0 -1
- package/dist/main/services/mcp/tools/context-tools.js.map +0 -1
- package/dist/main/services/mcp/tools/simulator-tools.d.ts.map +0 -1
- package/dist/main/services/mcp/tools/simulator-tools.js.map +0 -1
- package/dist/main/services/mcp/tools/workbench-tools.d.ts.map +0 -1
- package/dist/main/services/mcp/tools/workbench-tools.js.map +0 -1
- package/dist/main/services/notifications/renderer-notifier.d.ts.map +0 -1
- package/dist/main/services/notifications/renderer-notifier.js.map +0 -1
- package/dist/main/services/projects/index.d.ts.map +0 -1
- package/dist/main/services/projects/index.js.map +0 -1
- package/dist/main/services/projects/project-repository.d.ts.map +0 -1
- package/dist/main/services/projects/project-repository.js.map +0 -1
- package/dist/main/services/settings/index.d.ts.map +0 -1
- package/dist/main/services/settings/index.js.map +0 -1
- package/dist/main/services/simulator/dir.d.ts.map +0 -1
- package/dist/main/services/simulator/dir.js.map +0 -1
- package/dist/main/services/simulator/referer.d.ts.map +0 -1
- package/dist/main/services/simulator/referer.js.map +0 -1
- package/dist/main/services/simulator-storage/index.d.ts.map +0 -1
- package/dist/main/services/simulator-storage/index.js.map +0 -1
- package/dist/main/services/update/github-release-checker.d.ts.map +0 -1
- package/dist/main/services/update/github-release-checker.js.map +0 -1
- package/dist/main/services/update/index.d.ts.map +0 -1
- package/dist/main/services/update/index.js.map +0 -1
- package/dist/main/services/update/update-manager.d.ts.map +0 -1
- package/dist/main/services/update/update-manager.js.map +0 -1
- package/dist/main/services/views/view-manager.d.ts.map +0 -1
- package/dist/main/services/views/view-manager.js.map +0 -1
- package/dist/main/services/workbench-context.d.ts.map +0 -1
- package/dist/main/services/workbench-context.js.map +0 -1
- package/dist/main/services/workspace/index.d.ts.map +0 -1
- package/dist/main/services/workspace/index.js.map +0 -1
- package/dist/main/services/workspace/workspace-service.d.ts.map +0 -1
- package/dist/main/services/workspace/workspace-service.js.map +0 -1
- package/dist/main/utils/logger.d.ts.map +0 -1
- package/dist/main/utils/logger.js.map +0 -1
- package/dist/main/utils/paths.d.ts.map +0 -1
- package/dist/main/utils/paths.js.map +0 -1
- package/dist/main/utils/theme.d.ts.map +0 -1
- package/dist/main/utils/theme.js.map +0 -1
- package/dist/main/windows/main-window/create.d.ts.map +0 -1
- package/dist/main/windows/main-window/create.js.map +0 -1
- package/dist/main/windows/main-window/events.d.ts.map +0 -1
- package/dist/main/windows/main-window/events.js.map +0 -1
- package/dist/main/windows/main-window/index.d.ts.map +0 -1
- package/dist/main/windows/main-window/index.js.map +0 -1
- package/dist/main/windows/settings-window/create.d.ts.map +0 -1
- package/dist/main/windows/settings-window/create.js.map +0 -1
- package/dist/main/windows/settings-window/events.d.ts.map +0 -1
- package/dist/main/windows/settings-window/events.js.map +0 -1
- package/dist/main/windows/settings-window/index.d.ts.map +0 -1
- package/dist/main/windows/settings-window/index.js.map +0 -1
- package/dist/preload/instrumentation/app-data.d.ts.map +0 -1
- package/dist/preload/instrumentation/app-data.js.map +0 -1
- package/dist/preload/instrumentation/console.d.ts.map +0 -1
- package/dist/preload/instrumentation/console.js.map +0 -1
- package/dist/preload/instrumentation/disposable.d.ts.map +0 -1
- package/dist/preload/instrumentation/disposable.js.map +0 -1
- package/dist/preload/instrumentation/index.d.ts.map +0 -1
- package/dist/preload/instrumentation/index.js.map +0 -1
- package/dist/preload/instrumentation/wxml-extract.d.ts.map +0 -1
- package/dist/preload/instrumentation/wxml-extract.js.map +0 -1
- package/dist/preload/instrumentation/wxml.d.ts.map +0 -1
- package/dist/preload/instrumentation/wxml.js.map +0 -1
- package/dist/preload/runtime/bridge.d.ts.map +0 -1
- package/dist/preload/runtime/bridge.js.map +0 -1
- package/dist/preload/runtime/host.d.ts.map +0 -1
- package/dist/preload/runtime/host.js.map +0 -1
- package/dist/preload/shared/api-compat.d.ts.map +0 -1
- package/dist/preload/shared/api-compat.js.map +0 -1
- package/dist/preload/shared/constants.d.ts.map +0 -1
- package/dist/preload/shared/constants.js.map +0 -1
- package/dist/preload/shared/types.d.ts.map +0 -1
- package/dist/preload/shared/types.js.map +0 -1
- package/dist/preload/windows/main.d.ts.map +0 -1
- package/dist/preload/windows/main.js.map +0 -1
- package/dist/preload/windows/simulator.js.map +0 -7
- package/dist/renderer/assets/index-D_HFRqZA.js +0 -46
- package/dist/renderer/assets/index-D_HFRqZA.js.map +0 -1
- package/dist/renderer/assets/input-VLotwer2.js.map +0 -1
- package/dist/renderer/assets/ipc-transport-6Gy3FXIL.css +0 -1
- package/dist/renderer/assets/ipc-transport-COKIC6Ny.js.map +0 -1
- package/dist/renderer/assets/popover-Dn0RwMLI.js.map +0 -1
- package/dist/renderer/assets/select-DzIsLaKX.js.map +0 -1
- package/dist/renderer/assets/settings-CD-dacWB.js.map +0 -1
- package/dist/renderer/assets/settings-api-BV9MPckG.js.map +0 -1
- package/dist/renderer/assets/workbenchSettings-ClwgsvJL.js.map +0 -1
- package/dist/shared/constants.d.ts.map +0 -1
- package/dist/shared/constants.js.map +0 -1
- package/dist/shared/ipc-channels.d.ts.map +0 -1
- package/dist/shared/ipc-channels.js.map +0 -1
- package/dist/shared/types.d.ts.map +0 -1
- package/dist/shared/types.js.map +0 -1
- package/dist/simulator/assets/simulator-C1eHXnuq.js.map +0 -1
|
@@ -0,0 +1,2765 @@
|
|
|
1
|
+
// src/main/app/bootstrap.ts
|
|
2
|
+
import { app as app2 } from "electron";
|
|
3
|
+
|
|
4
|
+
// src/main/services/settings/index.ts
|
|
5
|
+
import { app, nativeTheme } from "electron";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
var DEFAULTS = {
|
|
9
|
+
cdp: {
|
|
10
|
+
enabled: false,
|
|
11
|
+
port: 9222
|
|
12
|
+
},
|
|
13
|
+
mcp: {
|
|
14
|
+
enabled: false,
|
|
15
|
+
port: 7789
|
|
16
|
+
},
|
|
17
|
+
theme: "system"
|
|
18
|
+
};
|
|
19
|
+
function getSettingsFile() {
|
|
20
|
+
return path.join(app.getPath("userData"), "dimina-workbench-settings.json");
|
|
21
|
+
}
|
|
22
|
+
function loadWorkbenchSettings() {
|
|
23
|
+
try {
|
|
24
|
+
const data = JSON.parse(fs.readFileSync(getSettingsFile(), "utf-8"));
|
|
25
|
+
const theme = ["system", "dark", "light"].includes(data.theme) ? data.theme : DEFAULTS.theme;
|
|
26
|
+
return {
|
|
27
|
+
cdp: {
|
|
28
|
+
enabled: data.cdp?.enabled ?? DEFAULTS.cdp.enabled,
|
|
29
|
+
port: data.cdp?.port ?? DEFAULTS.cdp.port
|
|
30
|
+
},
|
|
31
|
+
mcp: {
|
|
32
|
+
enabled: data.mcp?.enabled ?? DEFAULTS.mcp.enabled,
|
|
33
|
+
port: data.mcp?.port ?? DEFAULTS.mcp.port
|
|
34
|
+
},
|
|
35
|
+
theme
|
|
36
|
+
};
|
|
37
|
+
} catch {
|
|
38
|
+
return { ...DEFAULTS, cdp: { ...DEFAULTS.cdp }, mcp: { ...DEFAULTS.mcp } };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function saveWorkbenchSettings(settings) {
|
|
42
|
+
fs.writeFileSync(getSettingsFile(), JSON.stringify(settings, null, 2));
|
|
43
|
+
}
|
|
44
|
+
function applyTheme(theme) {
|
|
45
|
+
nativeTheme.themeSource = theme;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/main/app/bootstrap.ts
|
|
49
|
+
function setupCdpPort() {
|
|
50
|
+
if (app2.commandLine.getSwitchValue("remote-debugging-port")) return;
|
|
51
|
+
const envPort = process.env.DIMINA_DEBUG_PORT;
|
|
52
|
+
if (envPort) {
|
|
53
|
+
app2.commandLine.appendSwitch("remote-debugging-port", envPort);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const settings = loadWorkbenchSettings();
|
|
57
|
+
if (settings.cdp.enabled || settings.mcp.enabled) {
|
|
58
|
+
app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port));
|
|
59
|
+
} else if (!app2.isPackaged) {
|
|
60
|
+
app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port || 9222));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/main/app/app.ts
|
|
65
|
+
import { app as app11, BrowserWindow as BrowserWindow3, nativeImage } from "electron";
|
|
66
|
+
import fs5 from "fs";
|
|
67
|
+
import path6 from "path";
|
|
68
|
+
|
|
69
|
+
// src/main/utils/paths.ts
|
|
70
|
+
import fs2 from "fs";
|
|
71
|
+
import path2 from "path";
|
|
72
|
+
import { fileURLToPath } from "url";
|
|
73
|
+
function resolveDevtoolsPackageRoot() {
|
|
74
|
+
let dir = path2.dirname(fileURLToPath(import.meta.url));
|
|
75
|
+
const root = path2.parse(dir).root;
|
|
76
|
+
while (dir !== root) {
|
|
77
|
+
const pkgPath = path2.join(dir, "package.json");
|
|
78
|
+
try {
|
|
79
|
+
const pkg2 = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
|
|
80
|
+
if (pkg2.name === "@dimina-kit/devtools") return dir;
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
dir = path2.dirname(dir);
|
|
84
|
+
}
|
|
85
|
+
throw new Error("@dimina-kit/devtools package root not found from " + fileURLToPath(import.meta.url));
|
|
86
|
+
}
|
|
87
|
+
var devtoolsPackageRoot = resolveDevtoolsPackageRoot();
|
|
88
|
+
var rendererDir = path2.join(devtoolsPackageRoot, "dist/renderer");
|
|
89
|
+
var defaultPreloadPath = path2.join(devtoolsPackageRoot, "dist/preload/windows/simulator.js");
|
|
90
|
+
var simulatorDir = path2.join(devtoolsPackageRoot, "dist/simulator");
|
|
91
|
+
|
|
92
|
+
// src/main/app/lifecycle.ts
|
|
93
|
+
import { app as app3, globalShortcut } from "electron";
|
|
94
|
+
function registerAppLifecycle() {
|
|
95
|
+
app3.on("window-all-closed", () => {
|
|
96
|
+
globalShortcut.unregisterAll();
|
|
97
|
+
app3.quit();
|
|
98
|
+
});
|
|
99
|
+
app3.on("before-quit", () => {
|
|
100
|
+
globalShortcut.unregisterAll();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/main/windows/main-window/create.ts
|
|
105
|
+
import { app as app4, BrowserWindow, View, globalShortcut as globalShortcut2, session } from "electron";
|
|
106
|
+
|
|
107
|
+
// src/main/services/simulator/referer.ts
|
|
108
|
+
var DEFAULT_VERSION = "develop";
|
|
109
|
+
var cachedRefererUrl = null;
|
|
110
|
+
function buildServicewechatPageFrameReferer(appId, version = DEFAULT_VERSION) {
|
|
111
|
+
return `https://servicewechat.com/${appId}/${version}/page-frame.html`;
|
|
112
|
+
}
|
|
113
|
+
function setSimulatorServicewechatReferer(appId, version) {
|
|
114
|
+
cachedRefererUrl = buildServicewechatPageFrameReferer(
|
|
115
|
+
appId,
|
|
116
|
+
version && version.length > 0 ? version : DEFAULT_VERSION
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
function clearSimulatorServicewechatReferer() {
|
|
120
|
+
cachedRefererUrl = null;
|
|
121
|
+
}
|
|
122
|
+
function getSimulatorServicewechatReferer() {
|
|
123
|
+
return cachedRefererUrl;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/main/utils/theme.ts
|
|
127
|
+
import { nativeTheme as nativeTheme2 } from "electron";
|
|
128
|
+
function themeBg() {
|
|
129
|
+
return nativeTheme2.shouldUseDarkColors ? "#1a1a1a" : "#fafafa";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/main/windows/main-window/create.ts
|
|
133
|
+
var simulatorSessionConfigured = false;
|
|
134
|
+
function configureSimulatorSession() {
|
|
135
|
+
if (simulatorSessionConfigured) return;
|
|
136
|
+
simulatorSessionConfigured = true;
|
|
137
|
+
const simulatorSession = session.fromPartition("persist:simulator");
|
|
138
|
+
simulatorSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
|
139
|
+
const forcedReferer = getSimulatorServicewechatReferer();
|
|
140
|
+
if (forcedReferer) {
|
|
141
|
+
details.requestHeaders["Referer"] = forcedReferer;
|
|
142
|
+
}
|
|
143
|
+
callback({ requestHeaders: details.requestHeaders });
|
|
144
|
+
});
|
|
145
|
+
simulatorSession.webRequest.onHeadersReceived((details, callback) => {
|
|
146
|
+
const headers = details.responseHeaders ?? {};
|
|
147
|
+
headers["access-control-allow-origin"] = ["*"];
|
|
148
|
+
headers["access-control-allow-headers"] = ["*"];
|
|
149
|
+
headers["access-control-allow-methods"] = ["*"];
|
|
150
|
+
headers["Cross-Origin-Opener-Policy"] = ["same-origin"];
|
|
151
|
+
headers["Cross-Origin-Embedder-Policy"] = ["require-corp"];
|
|
152
|
+
callback({ responseHeaders: headers });
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function createMainWindow(opts) {
|
|
156
|
+
configureSimulatorSession();
|
|
157
|
+
const mainWindow = new BrowserWindow({
|
|
158
|
+
width: opts.width ?? 1280,
|
|
159
|
+
height: opts.height ?? 980,
|
|
160
|
+
minWidth: opts.minWidth ?? 1e3,
|
|
161
|
+
minHeight: opts.minHeight ?? 600,
|
|
162
|
+
title: opts.title ?? "Dimina DevTools",
|
|
163
|
+
show: false,
|
|
164
|
+
backgroundColor: themeBg(),
|
|
165
|
+
webPreferences: {
|
|
166
|
+
nodeIntegration: true,
|
|
167
|
+
contextIsolation: false,
|
|
168
|
+
webviewTag: true
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
mainWindow.once("ready-to-show", () => {
|
|
172
|
+
if (process.env.NODE_ENV === "test") {
|
|
173
|
+
mainWindow.showInactive();
|
|
174
|
+
} else {
|
|
175
|
+
mainWindow.show();
|
|
176
|
+
if (!app4.isPackaged) {
|
|
177
|
+
mainWindow.webContents.openDevTools({ mode: "detach" });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
mainWindow.webContents.on("will-attach-webview", (_event, webPreferences) => {
|
|
182
|
+
webPreferences.contextIsolation = false;
|
|
183
|
+
});
|
|
184
|
+
mainWindow.loadFile(opts.indexHtml);
|
|
185
|
+
const container = new View();
|
|
186
|
+
const mainWebView = mainWindow.contentView;
|
|
187
|
+
container.addChildView(mainWebView);
|
|
188
|
+
mainWindow.contentView = container;
|
|
189
|
+
globalShortcut2.register("CommandOrControl+Shift+I", () => {
|
|
190
|
+
mainWindow.webContents.openDevTools({ mode: "detach" });
|
|
191
|
+
});
|
|
192
|
+
return mainWindow;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/main/windows/main-window/events.ts
|
|
196
|
+
function wireMainWindowEvents(win, state = {}) {
|
|
197
|
+
const mainWebView = win.contentView.children[0];
|
|
198
|
+
const resizeMainWebView = () => {
|
|
199
|
+
if (mainWebView) {
|
|
200
|
+
const [w, h] = win.getContentSize();
|
|
201
|
+
mainWebView.setBounds({ x: 0, y: 0, width: w, height: h });
|
|
202
|
+
}
|
|
203
|
+
state.onResize?.();
|
|
204
|
+
};
|
|
205
|
+
resizeMainWebView();
|
|
206
|
+
win.on("resize", resizeMainWebView);
|
|
207
|
+
if (state.onClose) {
|
|
208
|
+
win.on("close", (event) => {
|
|
209
|
+
void state.onClose?.(event);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/main/services/default-adapter.ts
|
|
215
|
+
import path3 from "path";
|
|
216
|
+
import { app as app5 } from "electron";
|
|
217
|
+
var defaultAdapter = {
|
|
218
|
+
async openProject(opts) {
|
|
219
|
+
const diminaKit = await import("@dimina-kit/devkit");
|
|
220
|
+
const openProject = diminaKit.openProject;
|
|
221
|
+
return openProject({
|
|
222
|
+
outputDir: path3.join(app5.getPath("userData"), "dimina-fe-output"),
|
|
223
|
+
...opts,
|
|
224
|
+
simulatorDir
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// src/shared/ipc-channels.ts
|
|
230
|
+
var SimulatorChannel = {
|
|
231
|
+
Attach: "simulator:attach",
|
|
232
|
+
Detach: "simulator:detach",
|
|
233
|
+
Resize: "simulator:resize",
|
|
234
|
+
SetVisible: "simulator:setVisible",
|
|
235
|
+
Console: "simulator:console",
|
|
236
|
+
Wxml: "simulator:wxml",
|
|
237
|
+
AppData: "simulator:appdata",
|
|
238
|
+
AppDataAll: "simulator:appdata-all"
|
|
239
|
+
};
|
|
240
|
+
var SimulatorStorageChannel = {
|
|
241
|
+
GetSnapshot: "simulator:storage:snapshot",
|
|
242
|
+
Event: "simulator:storage:event"
|
|
243
|
+
};
|
|
244
|
+
var SimulatorElementChannel = {
|
|
245
|
+
Inspect: "simulator:element:inspect",
|
|
246
|
+
Clear: "simulator:element:clear"
|
|
247
|
+
};
|
|
248
|
+
var WorkbenchChannel = {
|
|
249
|
+
GetPanelConfig: "workbench:getPanelConfig",
|
|
250
|
+
GetApiNamespaces: "workbench:getApiNamespaces",
|
|
251
|
+
Reset: "workbench:reset"
|
|
252
|
+
};
|
|
253
|
+
var WorkbenchSettingsChannel = {
|
|
254
|
+
Get: "workbenchSettings:get",
|
|
255
|
+
Save: "workbenchSettings:save",
|
|
256
|
+
SetTheme: "workbenchSettings:setTheme",
|
|
257
|
+
GetCdpStatus: "workbenchSettings:getCdpStatus",
|
|
258
|
+
SetVisible: "workbenchSettings:setVisible",
|
|
259
|
+
Init: "workbenchSettings:init"
|
|
260
|
+
};
|
|
261
|
+
var ProjectChannel = {
|
|
262
|
+
Open: "project:open",
|
|
263
|
+
Close: "project:close",
|
|
264
|
+
GetPages: "project:getPages",
|
|
265
|
+
GetCompileConfig: "project:getCompileConfig",
|
|
266
|
+
SaveCompileConfig: "project:saveCompileConfig",
|
|
267
|
+
Status: "project:status"
|
|
268
|
+
};
|
|
269
|
+
var ProjectsChannel = {
|
|
270
|
+
List: "projects:list",
|
|
271
|
+
Add: "projects:add",
|
|
272
|
+
Remove: "projects:remove"
|
|
273
|
+
};
|
|
274
|
+
var DialogChannel = {
|
|
275
|
+
OpenDirectory: "dialog:openDirectory"
|
|
276
|
+
};
|
|
277
|
+
var PanelChannel = {
|
|
278
|
+
List: "panel:list",
|
|
279
|
+
Eval: "panel:eval",
|
|
280
|
+
Select: "panel:select",
|
|
281
|
+
SelectSimulator: "panel:selectSimulator"
|
|
282
|
+
};
|
|
283
|
+
var PopoverChannel = {
|
|
284
|
+
Show: "popover:show",
|
|
285
|
+
Hide: "popover:hide",
|
|
286
|
+
Relaunch: "popover:relaunch",
|
|
287
|
+
Closed: "popover:closed",
|
|
288
|
+
Init: "popover:init"
|
|
289
|
+
};
|
|
290
|
+
var ToolbarChannel = {
|
|
291
|
+
GetActions: "toolbar:getActions",
|
|
292
|
+
ActionsChanged: "toolbar:actionsChanged",
|
|
293
|
+
/** Prefix for dynamic action channels: `toolbar:action:${actionId}` */
|
|
294
|
+
ActionPrefix: "toolbar:action:"
|
|
295
|
+
};
|
|
296
|
+
var WindowChannel = {
|
|
297
|
+
NavigateBack: "window:navigateBack"
|
|
298
|
+
};
|
|
299
|
+
var AppChannel = {
|
|
300
|
+
GetPreloadPath: "app:getPreloadPath",
|
|
301
|
+
GetBranding: "app:getBranding"
|
|
302
|
+
};
|
|
303
|
+
var AutomationChannel = {
|
|
304
|
+
GetPort: "automation:port"
|
|
305
|
+
};
|
|
306
|
+
var SettingsChannel = {
|
|
307
|
+
SetVisible: "settings:setVisible",
|
|
308
|
+
ConfigChanged: "settings:configChanged",
|
|
309
|
+
ProjectSettingsChanged: "settings:projectSettingsChanged",
|
|
310
|
+
Init: "settings:init",
|
|
311
|
+
Closed: "settings:closed",
|
|
312
|
+
Changed: "settings:changed"
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// src/main/services/notifications/renderer-notifier.ts
|
|
316
|
+
function liveWebContents(wc) {
|
|
317
|
+
if (!wc) return null;
|
|
318
|
+
if (wc.isDestroyed()) return null;
|
|
319
|
+
return wc;
|
|
320
|
+
}
|
|
321
|
+
function createRendererNotifier(ctx) {
|
|
322
|
+
function sendToMain(channel, ...args) {
|
|
323
|
+
if (ctx.mainWindow.isDestroyed()) return;
|
|
324
|
+
const wc = liveWebContents(ctx.mainWindow.webContents);
|
|
325
|
+
if (!wc) return;
|
|
326
|
+
wc.send(channel, ...args);
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
projectStatus(payload) {
|
|
330
|
+
sendToMain(ProjectChannel.Status, payload);
|
|
331
|
+
},
|
|
332
|
+
windowNavigateBack() {
|
|
333
|
+
sendToMain(WindowChannel.NavigateBack);
|
|
334
|
+
},
|
|
335
|
+
settingsClosed() {
|
|
336
|
+
sendToMain(SettingsChannel.Closed);
|
|
337
|
+
},
|
|
338
|
+
settingsChanged(config) {
|
|
339
|
+
sendToMain(SettingsChannel.Changed, config);
|
|
340
|
+
},
|
|
341
|
+
popoverClosed() {
|
|
342
|
+
sendToMain(PopoverChannel.Closed);
|
|
343
|
+
},
|
|
344
|
+
popoverRelaunch(config) {
|
|
345
|
+
sendToMain(PopoverChannel.Relaunch, config);
|
|
346
|
+
},
|
|
347
|
+
workbenchReset() {
|
|
348
|
+
sendToMain(WorkbenchChannel.Reset);
|
|
349
|
+
},
|
|
350
|
+
toolbarActionsChanged() {
|
|
351
|
+
sendToMain(ToolbarChannel.ActionsChanged);
|
|
352
|
+
},
|
|
353
|
+
popoverInit(popoverView, payload) {
|
|
354
|
+
const wc = liveWebContents(popoverView.webContents);
|
|
355
|
+
if (!wc) return;
|
|
356
|
+
wc.send(PopoverChannel.Init, payload);
|
|
357
|
+
},
|
|
358
|
+
settingsInit(payload) {
|
|
359
|
+
const wc = liveWebContents(ctx.views.getSettingsWebContents());
|
|
360
|
+
if (!wc) return;
|
|
361
|
+
wc.send(SettingsChannel.Init, payload);
|
|
362
|
+
},
|
|
363
|
+
workbenchSettingsInit(window, payload) {
|
|
364
|
+
if (window.isDestroyed()) return;
|
|
365
|
+
const wc = liveWebContents(window.webContents);
|
|
366
|
+
if (!wc) return;
|
|
367
|
+
wc.send(WorkbenchSettingsChannel.Init, payload);
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/main/services/views/view-manager.ts
|
|
373
|
+
import { WebContentsView as WebContentsView2, webContents } from "electron";
|
|
374
|
+
import path4 from "path";
|
|
375
|
+
|
|
376
|
+
// src/main/services/layout/index.ts
|
|
377
|
+
var HEADER_H = 40;
|
|
378
|
+
var SPLITTER_W = 4;
|
|
379
|
+
function getRightX(simWidth) {
|
|
380
|
+
return simWidth + SPLITTER_W;
|
|
381
|
+
}
|
|
382
|
+
function computeRightPanelBounds(contentWidth, contentHeight, simWidth) {
|
|
383
|
+
const x = getRightX(simWidth);
|
|
384
|
+
return {
|
|
385
|
+
x,
|
|
386
|
+
y: HEADER_H,
|
|
387
|
+
width: Math.max(1, contentWidth - x),
|
|
388
|
+
height: Math.max(1, contentHeight - HEADER_H)
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function computeSimulatorBounds(contentWidth, contentHeight, simWidth) {
|
|
392
|
+
return computeRightPanelBounds(contentWidth, contentHeight, simWidth);
|
|
393
|
+
}
|
|
394
|
+
var SETTINGS_W = 320;
|
|
395
|
+
function computeSettingsBounds(contentWidth, contentHeight) {
|
|
396
|
+
return {
|
|
397
|
+
x: Math.max(0, contentWidth - SETTINGS_W),
|
|
398
|
+
y: HEADER_H,
|
|
399
|
+
width: SETTINGS_W,
|
|
400
|
+
height: Math.max(1, contentHeight - HEADER_H)
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
function computePopoverBounds(contentWidth, contentHeight) {
|
|
404
|
+
return {
|
|
405
|
+
x: 0,
|
|
406
|
+
y: HEADER_H,
|
|
407
|
+
width: contentWidth,
|
|
408
|
+
height: contentHeight - HEADER_H
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/main/services/views/view-manager.ts
|
|
413
|
+
function createViewManager(ctx) {
|
|
414
|
+
let simulatorView = null;
|
|
415
|
+
let simulatorViewAdded = false;
|
|
416
|
+
let settingsView = null;
|
|
417
|
+
let settingsViewAdded = false;
|
|
418
|
+
let popoverView = null;
|
|
419
|
+
let lastSimWidth = 375;
|
|
420
|
+
let simulatorWebContentsId = null;
|
|
421
|
+
function destroyViewInternal(view) {
|
|
422
|
+
if (!view) return;
|
|
423
|
+
if (!ctx.mainWindow.isDestroyed()) {
|
|
424
|
+
try {
|
|
425
|
+
ctx.mainWindow.contentView.removeChildView(view);
|
|
426
|
+
} catch {
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
if (!view.webContents.isDestroyed()) {
|
|
431
|
+
view.webContents.close();
|
|
432
|
+
}
|
|
433
|
+
} catch {
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function applySimulatorBounds(simWidth) {
|
|
437
|
+
if (!simulatorView || ctx.mainWindow.isDestroyed()) return;
|
|
438
|
+
const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
|
|
439
|
+
simulatorView.setBounds(computeSimulatorBounds(w, h, simWidth));
|
|
440
|
+
}
|
|
441
|
+
function applySettingsBounds() {
|
|
442
|
+
if (!settingsView || ctx.mainWindow.isDestroyed()) return;
|
|
443
|
+
const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
|
|
444
|
+
settingsView.setBounds(computeSettingsBounds(w, h));
|
|
445
|
+
}
|
|
446
|
+
function applyPopoverBounds() {
|
|
447
|
+
if (!popoverView || ctx.mainWindow.isDestroyed()) return;
|
|
448
|
+
const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
|
|
449
|
+
popoverView.setBounds(computePopoverBounds(w, h));
|
|
450
|
+
}
|
|
451
|
+
function attachSimulator(simWcId, simWidth) {
|
|
452
|
+
const sim = webContents.fromId(simWcId);
|
|
453
|
+
if (!sim) {
|
|
454
|
+
console.error("[workbench] attachSimulator \u2014 simWc not found for id", simWcId);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
lastSimWidth = simWidth;
|
|
458
|
+
simulatorWebContentsId = simWcId;
|
|
459
|
+
if (simulatorView) {
|
|
460
|
+
hideSimulator();
|
|
461
|
+
try {
|
|
462
|
+
if (!simulatorView.webContents.isDestroyed()) {
|
|
463
|
+
simulatorView.webContents.close();
|
|
464
|
+
}
|
|
465
|
+
} catch {
|
|
466
|
+
}
|
|
467
|
+
simulatorView = null;
|
|
468
|
+
}
|
|
469
|
+
simulatorView = new WebContentsView2();
|
|
470
|
+
sim.setDevToolsWebContents(simulatorView.webContents);
|
|
471
|
+
sim.openDevTools();
|
|
472
|
+
const devtoolsWc = simulatorView.webContents;
|
|
473
|
+
devtoolsWc.once("dom-ready", () => {
|
|
474
|
+
devtoolsWc.executeJavaScript(`
|
|
475
|
+
(function() {
|
|
476
|
+
try { localStorage.setItem('panel-selectedTab', '"console"') } catch {}
|
|
477
|
+
let tries = 0
|
|
478
|
+
const timer = setInterval(() => {
|
|
479
|
+
tries++
|
|
480
|
+
const tabs = document.querySelectorAll('[role="tab"], .tabbed-pane-header-tab')
|
|
481
|
+
for (const tab of tabs) {
|
|
482
|
+
const label = (tab.getAttribute('aria-label') || tab.textContent || '').trim().toLowerCase()
|
|
483
|
+
if (label === 'console' || label.startsWith('console')) {
|
|
484
|
+
tab.click()
|
|
485
|
+
clearInterval(timer)
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (tries > 40) clearInterval(timer)
|
|
490
|
+
}, 100)
|
|
491
|
+
})()
|
|
492
|
+
`).catch(() => {
|
|
493
|
+
});
|
|
494
|
+
});
|
|
495
|
+
if (getDefaultTab(ctx) === "simulator") {
|
|
496
|
+
ctx.mainWindow.contentView.addChildView(simulatorView);
|
|
497
|
+
simulatorViewAdded = true;
|
|
498
|
+
applySimulatorBounds(simWidth);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function detachSimulator() {
|
|
502
|
+
hidePopover();
|
|
503
|
+
destroyViewInternal(settingsView);
|
|
504
|
+
settingsView = null;
|
|
505
|
+
settingsViewAdded = false;
|
|
506
|
+
destroyViewInternal(simulatorView);
|
|
507
|
+
simulatorView = null;
|
|
508
|
+
simulatorViewAdded = false;
|
|
509
|
+
simulatorWebContentsId = null;
|
|
510
|
+
}
|
|
511
|
+
function showSimulator(simWidth) {
|
|
512
|
+
lastSimWidth = simWidth;
|
|
513
|
+
if (!simulatorView) return;
|
|
514
|
+
if (!simulatorViewAdded) {
|
|
515
|
+
ctx.mainWindow.contentView.addChildView(simulatorView);
|
|
516
|
+
simulatorViewAdded = true;
|
|
517
|
+
}
|
|
518
|
+
applySimulatorBounds(simWidth);
|
|
519
|
+
}
|
|
520
|
+
function hideSimulator() {
|
|
521
|
+
if (simulatorView && simulatorViewAdded) {
|
|
522
|
+
try {
|
|
523
|
+
ctx.mainWindow.contentView.removeChildView(simulatorView);
|
|
524
|
+
} catch (e) {
|
|
525
|
+
console.error("[workbench] hideSimulator error", e);
|
|
526
|
+
}
|
|
527
|
+
simulatorViewAdded = false;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async function showSettings() {
|
|
531
|
+
if (!settingsView) {
|
|
532
|
+
settingsView = new WebContentsView2({
|
|
533
|
+
webPreferences: { nodeIntegration: true, contextIsolation: false }
|
|
534
|
+
});
|
|
535
|
+
await settingsView.webContents.loadFile(
|
|
536
|
+
path4.join(ctx.rendererDir, "entries/settings/index.html")
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
if (!settingsViewAdded) {
|
|
540
|
+
ctx.mainWindow.contentView.addChildView(settingsView);
|
|
541
|
+
settingsViewAdded = true;
|
|
542
|
+
}
|
|
543
|
+
applySettingsBounds();
|
|
544
|
+
}
|
|
545
|
+
function hideSettings() {
|
|
546
|
+
if (settingsView && settingsViewAdded) {
|
|
547
|
+
try {
|
|
548
|
+
ctx.mainWindow.contentView.removeChildView(settingsView);
|
|
549
|
+
} catch {
|
|
550
|
+
}
|
|
551
|
+
settingsViewAdded = false;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function showPopover(data) {
|
|
555
|
+
hidePopover();
|
|
556
|
+
const popover = new WebContentsView2({
|
|
557
|
+
webPreferences: { nodeIntegration: true, contextIsolation: false }
|
|
558
|
+
});
|
|
559
|
+
popoverView = popover;
|
|
560
|
+
popover.setBackgroundColor("#00000000");
|
|
561
|
+
ctx.mainWindow.contentView.addChildView(popover);
|
|
562
|
+
applyPopoverBounds();
|
|
563
|
+
popover.webContents.once("did-finish-load", () => {
|
|
564
|
+
ctx.notify.popoverInit(popover, data);
|
|
565
|
+
});
|
|
566
|
+
popover.webContents.loadFile(
|
|
567
|
+
path4.join(ctx.rendererDir, "entries/popover/index.html")
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
function hidePopover() {
|
|
571
|
+
if (popoverView) {
|
|
572
|
+
destroyViewInternal(popoverView);
|
|
573
|
+
popoverView = null;
|
|
574
|
+
ctx.notify.popoverClosed();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function repositionAll() {
|
|
578
|
+
if (simulatorView && simulatorViewAdded)
|
|
579
|
+
applySimulatorBounds(lastSimWidth);
|
|
580
|
+
if (settingsView && settingsViewAdded)
|
|
581
|
+
applySettingsBounds();
|
|
582
|
+
if (popoverView)
|
|
583
|
+
applyPopoverBounds();
|
|
584
|
+
}
|
|
585
|
+
function disposeAll() {
|
|
586
|
+
detachSimulator();
|
|
587
|
+
}
|
|
588
|
+
function logChildViews(_label) {
|
|
589
|
+
}
|
|
590
|
+
function positionSimulator(simWidth) {
|
|
591
|
+
applySimulatorBounds(simWidth);
|
|
592
|
+
}
|
|
593
|
+
function positionSettings() {
|
|
594
|
+
applySettingsBounds();
|
|
595
|
+
}
|
|
596
|
+
function resize(simWidth) {
|
|
597
|
+
lastSimWidth = simWidth;
|
|
598
|
+
if (simulatorViewAdded) applySimulatorBounds(simWidth);
|
|
599
|
+
if (settingsViewAdded) applySettingsBounds();
|
|
600
|
+
}
|
|
601
|
+
function setVisible(visible, simWidth) {
|
|
602
|
+
lastSimWidth = simWidth;
|
|
603
|
+
if (!simulatorView) return;
|
|
604
|
+
if (visible && !simulatorViewAdded) {
|
|
605
|
+
showSimulator(simWidth);
|
|
606
|
+
} else if (!visible) {
|
|
607
|
+
hideSimulator();
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
return {
|
|
611
|
+
attachSimulator,
|
|
612
|
+
detachSimulator,
|
|
613
|
+
showSimulator,
|
|
614
|
+
hideSimulator,
|
|
615
|
+
showSettings,
|
|
616
|
+
hideSettings,
|
|
617
|
+
showPopover,
|
|
618
|
+
hidePopover,
|
|
619
|
+
repositionAll,
|
|
620
|
+
disposeAll,
|
|
621
|
+
logChildViews,
|
|
622
|
+
positionSimulator,
|
|
623
|
+
positionSettings,
|
|
624
|
+
getSimulatorWebContentsId: () => simulatorWebContentsId,
|
|
625
|
+
getLastSimWidth: () => lastSimWidth,
|
|
626
|
+
isSimulatorAdded: () => simulatorViewAdded,
|
|
627
|
+
hasSimulatorView: () => simulatorView !== null,
|
|
628
|
+
getSettingsWebContents: () => {
|
|
629
|
+
if (!settingsView) return null;
|
|
630
|
+
if (settingsView.webContents.isDestroyed()) return null;
|
|
631
|
+
return settingsView.webContents;
|
|
632
|
+
},
|
|
633
|
+
resize,
|
|
634
|
+
setVisible
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/main/services/projects/project-repository.ts
|
|
639
|
+
import { app as app6 } from "electron";
|
|
640
|
+
import path5 from "path";
|
|
641
|
+
import fs3 from "fs";
|
|
642
|
+
|
|
643
|
+
// src/shared/constants.ts
|
|
644
|
+
var DEFAULT_SCENE = 1001;
|
|
645
|
+
|
|
646
|
+
// src/main/utils/logger.ts
|
|
647
|
+
var LEVEL_PRIORITY = {
|
|
648
|
+
debug: 0,
|
|
649
|
+
info: 1,
|
|
650
|
+
warn: 2,
|
|
651
|
+
error: 3
|
|
652
|
+
};
|
|
653
|
+
var LEVEL_PREFIX = {
|
|
654
|
+
debug: "[DEBUG]",
|
|
655
|
+
info: "[INFO]",
|
|
656
|
+
warn: "[WARN]",
|
|
657
|
+
error: "[ERROR]"
|
|
658
|
+
};
|
|
659
|
+
var currentLevel = "debug";
|
|
660
|
+
function shouldLog(level) {
|
|
661
|
+
return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[currentLevel];
|
|
662
|
+
}
|
|
663
|
+
function formatMessage(level, tag, message) {
|
|
664
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
665
|
+
return `${ts} ${LEVEL_PREFIX[level]} [${tag}] ${message}`;
|
|
666
|
+
}
|
|
667
|
+
function createLogger(tag) {
|
|
668
|
+
return {
|
|
669
|
+
debug(message, ...args) {
|
|
670
|
+
if (shouldLog("debug")) console.debug(formatMessage("debug", tag, message), ...args);
|
|
671
|
+
},
|
|
672
|
+
info(message, ...args) {
|
|
673
|
+
if (shouldLog("info")) console.info(formatMessage("info", tag, message), ...args);
|
|
674
|
+
},
|
|
675
|
+
warn(message, ...args) {
|
|
676
|
+
if (shouldLog("warn")) console.warn(formatMessage("warn", tag, message), ...args);
|
|
677
|
+
},
|
|
678
|
+
error(message, ...args) {
|
|
679
|
+
if (shouldLog("error")) console.error(formatMessage("error", tag, message), ...args);
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// src/main/services/projects/project-repository.ts
|
|
685
|
+
var log = createLogger("projects");
|
|
686
|
+
function getProjectsFile() {
|
|
687
|
+
return path5.join(app6.getPath("userData"), "dimina-projects.json");
|
|
688
|
+
}
|
|
689
|
+
function load() {
|
|
690
|
+
try {
|
|
691
|
+
return JSON.parse(fs3.readFileSync(getProjectsFile(), "utf-8"));
|
|
692
|
+
} catch {
|
|
693
|
+
return [];
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function save(projects) {
|
|
697
|
+
fs3.writeFileSync(getProjectsFile(), JSON.stringify(projects, null, 2));
|
|
698
|
+
}
|
|
699
|
+
function listProjects() {
|
|
700
|
+
return load();
|
|
701
|
+
}
|
|
702
|
+
function validateProjectDir(dirPath) {
|
|
703
|
+
if (!dirPath) {
|
|
704
|
+
return "\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u8DEF\u5F84\u4E3A\u7A7A\uFF0C\u8BF7\u9009\u62E9\u5305\u542B\u5C0F\u7A0B\u5E8F\u6E90\u7801\u7684\u76EE\u5F55";
|
|
705
|
+
}
|
|
706
|
+
if (!fs3.existsSync(dirPath)) {
|
|
707
|
+
return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
|
|
708
|
+
}
|
|
709
|
+
if (!fs3.existsSync(path5.join(dirPath, "app.json"))) {
|
|
710
|
+
const configPath = path5.join(dirPath, "project.config.json");
|
|
711
|
+
if (fs3.existsSync(configPath)) {
|
|
712
|
+
try {
|
|
713
|
+
const cfg = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
714
|
+
if (cfg.miniprogramRoot) {
|
|
715
|
+
const resolvedRoot = path5.resolve(dirPath, cfg.miniprogramRoot);
|
|
716
|
+
return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
|
|
717
|
+
}
|
|
718
|
+
} catch (err) {
|
|
719
|
+
log.warn("Failed to parse project.config.json", err);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return "\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0C\u8BF7\u9009\u62E9\u5305\u542B\u5C0F\u7A0B\u5E8F\u6E90\u7801\u7684\u76EE\u5F55";
|
|
723
|
+
}
|
|
724
|
+
return null;
|
|
725
|
+
}
|
|
726
|
+
function addProject(dirPath) {
|
|
727
|
+
const projects = load();
|
|
728
|
+
let name = path5.basename(dirPath);
|
|
729
|
+
try {
|
|
730
|
+
const configPath = path5.join(dirPath, "project.config.json");
|
|
731
|
+
if (fs3.existsSync(configPath)) {
|
|
732
|
+
const cfg = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
733
|
+
if (cfg.projectname) name = cfg.projectname;
|
|
734
|
+
}
|
|
735
|
+
} catch (err) {
|
|
736
|
+
log.warn("Failed to read project name from config", err);
|
|
737
|
+
}
|
|
738
|
+
const project = { name, path: dirPath, lastOpened: null };
|
|
739
|
+
const idx = projects.findIndex((p) => p.path === dirPath);
|
|
740
|
+
if (idx >= 0) {
|
|
741
|
+
projects[idx] = { ...projects[idx], name };
|
|
742
|
+
} else {
|
|
743
|
+
projects.unshift(project);
|
|
744
|
+
}
|
|
745
|
+
save(projects);
|
|
746
|
+
return project;
|
|
747
|
+
}
|
|
748
|
+
function removeProject(dirPath) {
|
|
749
|
+
save(load().filter((p) => p.path !== dirPath));
|
|
750
|
+
}
|
|
751
|
+
function updateLastOpened(dirPath) {
|
|
752
|
+
const projects = load();
|
|
753
|
+
const idx = projects.findIndex((p) => p.path === dirPath);
|
|
754
|
+
if (idx >= 0) {
|
|
755
|
+
projects[idx] = { ...projects[idx], lastOpened: (/* @__PURE__ */ new Date()).toISOString() };
|
|
756
|
+
save(projects);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
function getCompileConfig(dirPath) {
|
|
760
|
+
const projects = load();
|
|
761
|
+
const project = projects.find((p) => p.path === dirPath);
|
|
762
|
+
return project?.compileConfig ?? {
|
|
763
|
+
startPage: "",
|
|
764
|
+
scene: DEFAULT_SCENE,
|
|
765
|
+
queryParams: []
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
function getProjectPages(dirPath) {
|
|
769
|
+
const appJsonPath = path5.join(dirPath, "app.json");
|
|
770
|
+
try {
|
|
771
|
+
const appJson = JSON.parse(
|
|
772
|
+
fs3.readFileSync(appJsonPath, "utf-8")
|
|
773
|
+
);
|
|
774
|
+
return {
|
|
775
|
+
pages: appJson.pages || [],
|
|
776
|
+
entryPagePath: appJson.entryPagePath || appJson.pages?.[0] || ""
|
|
777
|
+
};
|
|
778
|
+
} catch {
|
|
779
|
+
return { pages: [], entryPagePath: "" };
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
function saveCompileConfig(dirPath, config) {
|
|
783
|
+
const projects = load();
|
|
784
|
+
const idx = projects.findIndex((p) => p.path === dirPath);
|
|
785
|
+
if (idx >= 0) {
|
|
786
|
+
projects[idx] = { ...projects[idx], compileConfig: config };
|
|
787
|
+
save(projects);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function getProjectSettings(projectPath) {
|
|
791
|
+
if (!projectPath) {
|
|
792
|
+
return { uploadWithSourceMap: false };
|
|
793
|
+
}
|
|
794
|
+
try {
|
|
795
|
+
const configPath = path5.join(projectPath, "project.config.json");
|
|
796
|
+
const config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
797
|
+
return {
|
|
798
|
+
uploadWithSourceMap: !!config.setting?.uploadWithSourceMap
|
|
799
|
+
};
|
|
800
|
+
} catch {
|
|
801
|
+
return { uploadWithSourceMap: false };
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
function updateProjectSettings(projectPath, patch) {
|
|
805
|
+
if (!projectPath) return;
|
|
806
|
+
const configPath = path5.join(projectPath, "project.config.json");
|
|
807
|
+
let config = {};
|
|
808
|
+
try {
|
|
809
|
+
config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
810
|
+
} catch {
|
|
811
|
+
}
|
|
812
|
+
const nextSetting = {
|
|
813
|
+
...config.setting ?? {},
|
|
814
|
+
...patch.uploadWithSourceMap === void 0 ? {} : { uploadWithSourceMap: patch.uploadWithSourceMap }
|
|
815
|
+
};
|
|
816
|
+
fs3.writeFileSync(
|
|
817
|
+
configPath,
|
|
818
|
+
JSON.stringify({ ...config, setting: nextSetting }, null, 2)
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// src/main/services/workspace/workspace-service.ts
|
|
823
|
+
function createWorkspaceService(ctx) {
|
|
824
|
+
let currentSession = null;
|
|
825
|
+
let currentProjectPath = "";
|
|
826
|
+
function sendStatus(status, message) {
|
|
827
|
+
ctx.notify.projectStatus({ status, message });
|
|
828
|
+
}
|
|
829
|
+
function bestEffort(label, fn) {
|
|
830
|
+
try {
|
|
831
|
+
fn();
|
|
832
|
+
} catch (err) {
|
|
833
|
+
console.warn(`[workspace] ${label} failed (non-fatal):`, err);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
async function disposeSession() {
|
|
837
|
+
if (!currentSession) return;
|
|
838
|
+
const session2 = currentSession;
|
|
839
|
+
currentSession = null;
|
|
840
|
+
try {
|
|
841
|
+
await Promise.race([
|
|
842
|
+
session2.close(),
|
|
843
|
+
new Promise(
|
|
844
|
+
(_, reject) => setTimeout(() => reject(new Error("session close timed out")), 5e3)
|
|
845
|
+
)
|
|
846
|
+
]);
|
|
847
|
+
} catch (err) {
|
|
848
|
+
console.error("[workspace] session close failed:", err);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
function applyRefererFromSession(session2) {
|
|
852
|
+
const appInfo = session2.appInfo;
|
|
853
|
+
if (appInfo && typeof appInfo.appId === "string" && appInfo.appId.length > 0) {
|
|
854
|
+
setSimulatorServicewechatReferer(
|
|
855
|
+
appInfo.appId,
|
|
856
|
+
typeof appInfo.version === "string" ? appInfo.version : void 0
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return {
|
|
861
|
+
listProjects: () => listProjects(),
|
|
862
|
+
addProject: (dirPath) => addProject(dirPath),
|
|
863
|
+
removeProject: (dirPath) => removeProject(dirPath),
|
|
864
|
+
validateProjectDir: (dirPath) => validateProjectDir(dirPath),
|
|
865
|
+
async openProject(projectPath) {
|
|
866
|
+
clearSimulatorServicewechatReferer();
|
|
867
|
+
await disposeSession();
|
|
868
|
+
currentProjectPath = "";
|
|
869
|
+
const dirError = validateProjectDir(projectPath);
|
|
870
|
+
if (dirError) {
|
|
871
|
+
sendStatus("error", dirError);
|
|
872
|
+
return { success: false, error: dirError };
|
|
873
|
+
}
|
|
874
|
+
sendStatus("compiling", "\u6B63\u5728\u7F16\u8BD1...");
|
|
875
|
+
let session2;
|
|
876
|
+
try {
|
|
877
|
+
session2 = await ctx.adapter.openProject({
|
|
878
|
+
projectPath,
|
|
879
|
+
sourcemap: true,
|
|
880
|
+
onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u70ED\u66F4\u65B0"),
|
|
881
|
+
onBuildError: (err) => sendStatus("error", String(err))
|
|
882
|
+
});
|
|
883
|
+
} catch (err) {
|
|
884
|
+
clearSimulatorServicewechatReferer();
|
|
885
|
+
sendStatus("error", String(err));
|
|
886
|
+
return { success: false, error: String(err) };
|
|
887
|
+
}
|
|
888
|
+
currentSession = session2;
|
|
889
|
+
currentProjectPath = projectPath;
|
|
890
|
+
bestEffort("updateLastOpened", () => updateLastOpened(projectPath));
|
|
891
|
+
bestEffort("sendStatus", () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210"));
|
|
892
|
+
bestEffort("applyReferer", () => applyRefererFromSession(session2));
|
|
893
|
+
return {
|
|
894
|
+
success: true,
|
|
895
|
+
port: session2.port,
|
|
896
|
+
appInfo: session2.appInfo
|
|
897
|
+
};
|
|
898
|
+
},
|
|
899
|
+
async closeProject() {
|
|
900
|
+
clearSimulatorServicewechatReferer();
|
|
901
|
+
await disposeSession();
|
|
902
|
+
currentProjectPath = "";
|
|
903
|
+
ctx.views.disposeAll();
|
|
904
|
+
},
|
|
905
|
+
getSession: () => currentSession,
|
|
906
|
+
getProjectPath: () => currentProjectPath,
|
|
907
|
+
hasActiveSession: () => currentSession !== null,
|
|
908
|
+
getProjectPages: (projectPath) => getProjectPages(projectPath),
|
|
909
|
+
getCompileConfig: (projectPath) => getCompileConfig(projectPath),
|
|
910
|
+
saveCompileConfig: (projectPath, config) => saveCompileConfig(projectPath, config),
|
|
911
|
+
getProjectSettings: (projectPath) => getProjectSettings(projectPath),
|
|
912
|
+
updateProjectSettings: (projectPath, patch) => updateProjectSettings(projectPath, patch)
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// src/main/services/workbench-context.ts
|
|
917
|
+
function hasBuiltinPanel(ctx, panelId) {
|
|
918
|
+
return ctx.panels.includes(panelId);
|
|
919
|
+
}
|
|
920
|
+
function getDefaultTab(ctx) {
|
|
921
|
+
if (hasBuiltinPanel(ctx, "console")) return "simulator";
|
|
922
|
+
if (ctx.panels.length > 0) return ctx.panels[0];
|
|
923
|
+
return "simulator";
|
|
924
|
+
}
|
|
925
|
+
function createWorkbenchContext(opts) {
|
|
926
|
+
const ctx = {
|
|
927
|
+
mainWindow: opts.mainWindow,
|
|
928
|
+
adapter: opts.adapter ?? defaultAdapter,
|
|
929
|
+
preloadPath: opts.preloadPath,
|
|
930
|
+
rendererDir: opts.rendererDir,
|
|
931
|
+
panels: opts.panels ?? ["wxml", "console", "appdata", "storage"],
|
|
932
|
+
apiNamespaces: opts.apiNamespaces ?? [],
|
|
933
|
+
appName: opts.appName ?? "Dimina DevTools",
|
|
934
|
+
toolbarActions: opts.toolbarActions,
|
|
935
|
+
brandingProvider: opts.brandingProvider,
|
|
936
|
+
workbenchSettingsWindow: null
|
|
937
|
+
};
|
|
938
|
+
ctx.views = createViewManager(ctx);
|
|
939
|
+
ctx.notify = createRendererNotifier(ctx);
|
|
940
|
+
ctx.workspace = createWorkspaceService(ctx);
|
|
941
|
+
return ctx;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// src/main/menu/index.ts
|
|
945
|
+
import { Menu } from "electron";
|
|
946
|
+
function installAppMenu(ctx) {
|
|
947
|
+
const template = [
|
|
948
|
+
{
|
|
949
|
+
label: "Dimina DevTools",
|
|
950
|
+
submenu: [
|
|
951
|
+
{
|
|
952
|
+
label: "\u5F00\u53D1\u5DE5\u5177\u8BBE\u7F6E",
|
|
953
|
+
click: () => {
|
|
954
|
+
void openSettingsWindow(ctx).catch(() => {
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
{ type: "separator" },
|
|
959
|
+
{ role: "about" },
|
|
960
|
+
{ type: "separator" },
|
|
961
|
+
{ role: "services" },
|
|
962
|
+
{ type: "separator" },
|
|
963
|
+
{ role: "hide" },
|
|
964
|
+
{ role: "hideOthers" },
|
|
965
|
+
{ role: "unhide" },
|
|
966
|
+
{ type: "separator" },
|
|
967
|
+
{ role: "quit" }
|
|
968
|
+
]
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
label: "\u7F16\u8F91",
|
|
972
|
+
submenu: [
|
|
973
|
+
{ role: "undo" },
|
|
974
|
+
{ role: "redo" },
|
|
975
|
+
{ type: "separator" },
|
|
976
|
+
{ role: "cut" },
|
|
977
|
+
{ role: "copy" },
|
|
978
|
+
{ role: "paste" },
|
|
979
|
+
{ role: "selectAll" }
|
|
980
|
+
]
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
label: "\u89C6\u56FE",
|
|
984
|
+
submenu: [
|
|
985
|
+
{ role: "reload" },
|
|
986
|
+
{ role: "forceReload" },
|
|
987
|
+
{ role: "toggleDevTools" },
|
|
988
|
+
{ type: "separator" },
|
|
989
|
+
{ role: "resetZoom" },
|
|
990
|
+
{ role: "zoomIn" },
|
|
991
|
+
{ role: "zoomOut" },
|
|
992
|
+
{ type: "separator" },
|
|
993
|
+
{ role: "togglefullscreen" }
|
|
994
|
+
]
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
label: "\u7A97\u53E3",
|
|
998
|
+
submenu: [
|
|
999
|
+
{ role: "minimize" },
|
|
1000
|
+
{ role: "zoom" },
|
|
1001
|
+
{ type: "separator" },
|
|
1002
|
+
{ role: "front" },
|
|
1003
|
+
{ role: "window" }
|
|
1004
|
+
]
|
|
1005
|
+
}
|
|
1006
|
+
];
|
|
1007
|
+
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/main/ipc/app.ts
|
|
1011
|
+
import { ipcMain } from "electron";
|
|
1012
|
+
import fs4 from "fs";
|
|
1013
|
+
function registerAppIpc(ctx) {
|
|
1014
|
+
ipcMain.handle(AppChannel.GetPreloadPath, () => {
|
|
1015
|
+
return `file://${fs4.realpathSync(ctx.preloadPath)}`;
|
|
1016
|
+
});
|
|
1017
|
+
ipcMain.handle(AppChannel.GetBranding, async () => {
|
|
1018
|
+
if (ctx.brandingProvider) return ctx.brandingProvider();
|
|
1019
|
+
return { appName: ctx.appName };
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// src/main/ipc/simulator.ts
|
|
1024
|
+
import { ipcMain as ipcMain2 } from "electron";
|
|
1025
|
+
function registerSimulatorIpc(ctx) {
|
|
1026
|
+
ipcMain2.handle(SimulatorChannel.Attach, (_, simWcId, simWidth) => {
|
|
1027
|
+
ctx.views.attachSimulator(simWcId, simWidth);
|
|
1028
|
+
});
|
|
1029
|
+
ipcMain2.handle(SimulatorChannel.Detach, () => {
|
|
1030
|
+
ctx.views.detachSimulator();
|
|
1031
|
+
});
|
|
1032
|
+
ipcMain2.handle(SimulatorChannel.Resize, (_, simWidth) => {
|
|
1033
|
+
ctx.views.resize(simWidth);
|
|
1034
|
+
});
|
|
1035
|
+
ipcMain2.handle(SimulatorChannel.SetVisible, (_, visible, simWidth) => {
|
|
1036
|
+
ctx.views.setVisible(visible, simWidth);
|
|
1037
|
+
});
|
|
1038
|
+
ipcMain2.handle(WorkbenchChannel.GetPanelConfig, () => {
|
|
1039
|
+
return ctx.panels;
|
|
1040
|
+
});
|
|
1041
|
+
ipcMain2.handle(WorkbenchChannel.GetApiNamespaces, () => {
|
|
1042
|
+
return ctx.apiNamespaces;
|
|
1043
|
+
});
|
|
1044
|
+
ipcMain2.on(WorkbenchChannel.Reset, () => {
|
|
1045
|
+
ctx.notify.workbenchReset();
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// src/main/ipc/panels.ts
|
|
1050
|
+
import { ipcMain as ipcMain3, webContents as webContents2 } from "electron";
|
|
1051
|
+
var BUILTIN_PANELS = [
|
|
1052
|
+
{ id: "wxml", label: "WXML" },
|
|
1053
|
+
{ id: "appdata", label: "AppData" },
|
|
1054
|
+
{ id: "storage", label: "Storage" }
|
|
1055
|
+
];
|
|
1056
|
+
function registerPanelsIpc(ctx) {
|
|
1057
|
+
ipcMain3.handle(PanelChannel.List, () => {
|
|
1058
|
+
return BUILTIN_PANELS.filter((p) => ctx.panels.includes(p.id));
|
|
1059
|
+
});
|
|
1060
|
+
ipcMain3.handle(PanelChannel.Eval, async (_, expression) => {
|
|
1061
|
+
const simWcId = ctx.views.getSimulatorWebContentsId();
|
|
1062
|
+
if (!ctx.workspace.hasActiveSession() || simWcId == null) return void 0;
|
|
1063
|
+
const sim = webContents2.fromId(simWcId);
|
|
1064
|
+
if (!sim || sim.isDestroyed()) return void 0;
|
|
1065
|
+
try {
|
|
1066
|
+
return await sim.executeJavaScript(expression);
|
|
1067
|
+
} catch {
|
|
1068
|
+
return void 0;
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
ipcMain3.handle(PanelChannel.Select, (_event, _panelId) => {
|
|
1072
|
+
if (ctx.views.hasSimulatorView() && ctx.views.isSimulatorAdded()) {
|
|
1073
|
+
ctx.views.hideSimulator();
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
ipcMain3.handle(PanelChannel.SelectSimulator, () => {
|
|
1077
|
+
if (ctx.views.hasSimulatorView() && !ctx.views.isSimulatorAdded()) {
|
|
1078
|
+
ctx.views.showSimulator(ctx.views.getLastSimWidth());
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// src/main/ipc/popover.ts
|
|
1084
|
+
import { ipcMain as ipcMain4 } from "electron";
|
|
1085
|
+
function registerPopoverIpc(ctx) {
|
|
1086
|
+
ipcMain4.handle(PopoverChannel.Show, (_event, data) => {
|
|
1087
|
+
ctx.views.showPopover(data);
|
|
1088
|
+
});
|
|
1089
|
+
ipcMain4.handle(PopoverChannel.Hide, () => ctx.views.hidePopover());
|
|
1090
|
+
ipcMain4.on(PopoverChannel.Relaunch, (_, newConfig) => {
|
|
1091
|
+
ctx.views.hidePopover();
|
|
1092
|
+
ctx.notify.popoverRelaunch(newConfig);
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// src/main/ipc/projects.ts
|
|
1097
|
+
import { ipcMain as ipcMain5, dialog } from "electron";
|
|
1098
|
+
function registerProjectsIpc(ctx) {
|
|
1099
|
+
ipcMain5.handle(ProjectsChannel.List, () => ctx.workspace.listProjects());
|
|
1100
|
+
ipcMain5.handle(DialogChannel.OpenDirectory, async () => {
|
|
1101
|
+
const result = await dialog.showOpenDialog(ctx.mainWindow, {
|
|
1102
|
+
properties: ["openDirectory"],
|
|
1103
|
+
title: "\u9009\u62E9\u5C0F\u7A0B\u5E8F\u9879\u76EE\u76EE\u5F55"
|
|
1104
|
+
});
|
|
1105
|
+
return result.canceled ? null : result.filePaths[0];
|
|
1106
|
+
});
|
|
1107
|
+
ipcMain5.handle(
|
|
1108
|
+
ProjectsChannel.Add,
|
|
1109
|
+
(_, dirPath) => ctx.workspace.addProject(dirPath)
|
|
1110
|
+
);
|
|
1111
|
+
ipcMain5.handle(
|
|
1112
|
+
ProjectsChannel.Remove,
|
|
1113
|
+
(_, dirPath) => ctx.workspace.removeProject(dirPath)
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// src/main/ipc/session.ts
|
|
1118
|
+
import { ipcMain as ipcMain6 } from "electron";
|
|
1119
|
+
function registerSessionIpc(ctx) {
|
|
1120
|
+
ipcMain6.handle(
|
|
1121
|
+
ProjectChannel.Open,
|
|
1122
|
+
(_, projectPath) => ctx.workspace.openProject(projectPath)
|
|
1123
|
+
);
|
|
1124
|
+
ipcMain6.handle(
|
|
1125
|
+
ProjectChannel.GetPages,
|
|
1126
|
+
(_, projectPath) => ctx.workspace.getProjectPages(projectPath)
|
|
1127
|
+
);
|
|
1128
|
+
ipcMain6.handle(
|
|
1129
|
+
ProjectChannel.GetCompileConfig,
|
|
1130
|
+
(_, projectPath) => ctx.workspace.getCompileConfig(projectPath)
|
|
1131
|
+
);
|
|
1132
|
+
ipcMain6.handle(
|
|
1133
|
+
ProjectChannel.SaveCompileConfig,
|
|
1134
|
+
(_, projectPath, config) => ctx.workspace.saveCompileConfig(projectPath, config)
|
|
1135
|
+
);
|
|
1136
|
+
ipcMain6.handle(ProjectChannel.Close, () => ctx.workspace.closeProject());
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// src/main/ipc/settings.ts
|
|
1140
|
+
import { app as app7, ipcMain as ipcMain7 } from "electron";
|
|
1141
|
+
function registerSettingsIpc(ctx) {
|
|
1142
|
+
ipcMain7.handle(WorkbenchSettingsChannel.Get, () => {
|
|
1143
|
+
return loadWorkbenchSettings();
|
|
1144
|
+
});
|
|
1145
|
+
ipcMain7.handle(WorkbenchSettingsChannel.Save, (_, settings) => {
|
|
1146
|
+
saveWorkbenchSettings(settings);
|
|
1147
|
+
applyTheme(settings.theme ?? "system");
|
|
1148
|
+
return { success: true };
|
|
1149
|
+
});
|
|
1150
|
+
ipcMain7.handle(WorkbenchSettingsChannel.SetTheme, (_, theme) => {
|
|
1151
|
+
applyTheme(theme);
|
|
1152
|
+
});
|
|
1153
|
+
ipcMain7.handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
|
|
1154
|
+
const settings = loadWorkbenchSettings();
|
|
1155
|
+
const switchValue = app7.commandLine.getSwitchValue("remote-debugging-port");
|
|
1156
|
+
const implicitDevDefault = !app7.isPackaged && !settings.cdp.enabled && switchValue === "9222";
|
|
1157
|
+
return {
|
|
1158
|
+
configured: settings.cdp.enabled,
|
|
1159
|
+
port: settings.cdp.port,
|
|
1160
|
+
active: !!switchValue,
|
|
1161
|
+
activePort: switchValue ? parseInt(switchValue, 10) : null,
|
|
1162
|
+
implicitDevDefault
|
|
1163
|
+
};
|
|
1164
|
+
});
|
|
1165
|
+
ipcMain7.handle(WorkbenchSettingsChannel.SetVisible, async (_, visible) => {
|
|
1166
|
+
if (visible) {
|
|
1167
|
+
await openSettingsWindow(ctx);
|
|
1168
|
+
} else {
|
|
1169
|
+
ctx.workbenchSettingsWindow?.close();
|
|
1170
|
+
}
|
|
1171
|
+
});
|
|
1172
|
+
ipcMain7.handle(SettingsChannel.SetVisible, async (_, visible) => {
|
|
1173
|
+
if (visible) {
|
|
1174
|
+
await ctx.views.showSettings();
|
|
1175
|
+
ctx.notify.settingsInit({
|
|
1176
|
+
projectPath: ctx.workspace.getProjectPath(),
|
|
1177
|
+
config: ctx.workspace.getCompileConfig(ctx.workspace.getProjectPath()),
|
|
1178
|
+
projectSettings: ctx.workspace.getProjectSettings(ctx.workspace.getProjectPath())
|
|
1179
|
+
});
|
|
1180
|
+
} else {
|
|
1181
|
+
ctx.views.hideSettings();
|
|
1182
|
+
ctx.notify.settingsClosed();
|
|
1183
|
+
}
|
|
1184
|
+
});
|
|
1185
|
+
ipcMain7.on(SettingsChannel.ConfigChanged, (_, config) => {
|
|
1186
|
+
if (ctx.workspace.getProjectPath()) {
|
|
1187
|
+
ctx.workspace.saveCompileConfig(ctx.workspace.getProjectPath(), config);
|
|
1188
|
+
}
|
|
1189
|
+
ctx.notify.settingsChanged(config);
|
|
1190
|
+
});
|
|
1191
|
+
ipcMain7.on(
|
|
1192
|
+
SettingsChannel.ProjectSettingsChanged,
|
|
1193
|
+
(_, patch) => {
|
|
1194
|
+
ctx.workspace.updateProjectSettings(ctx.workspace.getProjectPath(), patch);
|
|
1195
|
+
}
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// src/main/ipc/toolbar.ts
|
|
1200
|
+
import { ipcMain as ipcMain8 } from "electron";
|
|
1201
|
+
function registerToolbarIpc(ctx) {
|
|
1202
|
+
ipcMain8.handle(ToolbarChannel.GetActions, async () => {
|
|
1203
|
+
if (ctx.toolbarActions) return ctx.toolbarActions();
|
|
1204
|
+
return [];
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// src/main/services/automation/index.ts
|
|
1209
|
+
import { ipcMain as ipcMain9 } from "electron";
|
|
1210
|
+
import { WebSocketServer } from "ws";
|
|
1211
|
+
|
|
1212
|
+
// src/main/services/automation/exec.ts
|
|
1213
|
+
import { webContents as webContents3 } from "electron";
|
|
1214
|
+
function getSimulator(ctx) {
|
|
1215
|
+
const simWcId = ctx.views.getSimulatorWebContentsId();
|
|
1216
|
+
if (!ctx.workspace.hasActiveSession() || simWcId == null) return null;
|
|
1217
|
+
const sim = webContents3.fromId(simWcId);
|
|
1218
|
+
if (!sim || sim.isDestroyed()) return null;
|
|
1219
|
+
return sim;
|
|
1220
|
+
}
|
|
1221
|
+
async function evalInSim(ctx, expression) {
|
|
1222
|
+
const sim = getSimulator(ctx);
|
|
1223
|
+
if (!sim) throw new Error("Simulator not connected");
|
|
1224
|
+
return sim.executeJavaScript(expression);
|
|
1225
|
+
}
|
|
1226
|
+
function buildElAccess(ref, varName = "el") {
|
|
1227
|
+
const escaped = ref.selector.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
1228
|
+
return `const ${varName} = _doc.querySelectorAll('${escaped}')[${ref.index}]`;
|
|
1229
|
+
}
|
|
1230
|
+
function inIframe(code) {
|
|
1231
|
+
return `(() => {
|
|
1232
|
+
const iframes = document.querySelectorAll('iframe')
|
|
1233
|
+
const iframe = iframes[iframes.length - 1]
|
|
1234
|
+
if (!iframe || !iframe.contentDocument) throw new Error('No page iframe')
|
|
1235
|
+
const _doc = iframe.contentDocument
|
|
1236
|
+
return (function() { ${code} })()
|
|
1237
|
+
})()`;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// src/main/services/automation/handlers/tool.ts
|
|
1241
|
+
var toolHandlers = {};
|
|
1242
|
+
toolHandlers["Tool.getInfo"] = async () => ({ SDKVersion: "2.7.3" });
|
|
1243
|
+
toolHandlers["Tool.close"] = async (ctx) => {
|
|
1244
|
+
await ctx.workspace.closeProject();
|
|
1245
|
+
return {};
|
|
1246
|
+
};
|
|
1247
|
+
|
|
1248
|
+
// src/main/services/automation/handlers/app.ts
|
|
1249
|
+
var appHandlers = {};
|
|
1250
|
+
appHandlers["App.getCurrentPage"] = async (ctx) => {
|
|
1251
|
+
const hash = await evalInSim(ctx, "location.hash");
|
|
1252
|
+
const clean = hash.replace(/^#/, "");
|
|
1253
|
+
const qIdx = clean.indexOf("?");
|
|
1254
|
+
const pathPart = qIdx >= 0 ? clean.substring(0, qIdx) : clean;
|
|
1255
|
+
const queryPart = qIdx >= 0 ? clean.substring(qIdx + 1) : "";
|
|
1256
|
+
let pagePath;
|
|
1257
|
+
if (pathPart.includes("|")) {
|
|
1258
|
+
const parts = pathPart.split("|");
|
|
1259
|
+
pagePath = parts[parts.length - 1] ?? "";
|
|
1260
|
+
} else {
|
|
1261
|
+
const slashIdx = pathPart.indexOf("/");
|
|
1262
|
+
pagePath = slashIdx >= 0 ? pathPart.substring(slashIdx + 1) : pathPart;
|
|
1263
|
+
}
|
|
1264
|
+
const query = {};
|
|
1265
|
+
if (queryPart) {
|
|
1266
|
+
for (const pair of queryPart.split("&")) {
|
|
1267
|
+
const [k, v] = pair.split("=");
|
|
1268
|
+
if (k) query[k] = decodeURIComponent(v || "");
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
const iframeCount = await evalInSim(ctx, `document.querySelectorAll('iframe').length`);
|
|
1272
|
+
return { pageId: iframeCount, path: pagePath, query };
|
|
1273
|
+
};
|
|
1274
|
+
appHandlers["App.getPageStack"] = async (ctx) => {
|
|
1275
|
+
const hash = await evalInSim(ctx, "location.hash");
|
|
1276
|
+
const clean = hash.replace(/^#/, "").replace(/\?.*$/, "");
|
|
1277
|
+
const pageStack = [];
|
|
1278
|
+
if (clean.includes("|")) {
|
|
1279
|
+
const parts = clean.split("|");
|
|
1280
|
+
for (let i = 1; i < parts.length; i++) {
|
|
1281
|
+
pageStack.push({ pageId: i, path: parts[i] ?? "", query: {} });
|
|
1282
|
+
}
|
|
1283
|
+
} else {
|
|
1284
|
+
const slashIdx = clean.indexOf("/");
|
|
1285
|
+
const currentPath = slashIdx >= 0 ? clean.substring(slashIdx + 1) : clean;
|
|
1286
|
+
if (currentPath) {
|
|
1287
|
+
pageStack.push({ pageId: 1, path: currentPath, query: {} });
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
return { pageStack };
|
|
1291
|
+
};
|
|
1292
|
+
appHandlers["App.callWxMethod"] = async (ctx, params) => {
|
|
1293
|
+
const method = params.method;
|
|
1294
|
+
const args = params.args || [];
|
|
1295
|
+
if (["navigateTo", "redirectTo", "reLaunch", "switchTab"].includes(method)) {
|
|
1296
|
+
const opts = args[0];
|
|
1297
|
+
const url = opts?.url;
|
|
1298
|
+
if (url) {
|
|
1299
|
+
const cleanUrl = url.startsWith("/") ? url : `/${url}`;
|
|
1300
|
+
const clicked = await evalInSim(ctx, inIframe(`
|
|
1301
|
+
const el = _doc.querySelector('[data-path="${cleanUrl}"]')
|
|
1302
|
+
if (el) { el.click(); return true }
|
|
1303
|
+
return false
|
|
1304
|
+
`)).catch(() => false);
|
|
1305
|
+
if (!clicked) {
|
|
1306
|
+
const hash = await evalInSim(ctx, "location.hash");
|
|
1307
|
+
const appId = hash.replace(/^#/, "").split("/")[0];
|
|
1308
|
+
if (method === "reLaunch" || method === "redirectTo") {
|
|
1309
|
+
await evalInSim(ctx, `location.href = location.pathname + '#${appId}${cleanUrl}'`);
|
|
1310
|
+
} else {
|
|
1311
|
+
await evalInSim(ctx, `location.hash = '#${appId}${cleanUrl}'`);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
1315
|
+
}
|
|
1316
|
+
return { result: void 0 };
|
|
1317
|
+
}
|
|
1318
|
+
if (method === "navigateBack") {
|
|
1319
|
+
await evalInSim(ctx, "history.back()");
|
|
1320
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
1321
|
+
return { result: void 0 };
|
|
1322
|
+
}
|
|
1323
|
+
const argsStr = args.map((a) => JSON.stringify(a)).join(", ");
|
|
1324
|
+
const result = await evalInSim(ctx, `
|
|
1325
|
+
new Promise((resolve, reject) => {
|
|
1326
|
+
try {
|
|
1327
|
+
const r = wx.${method}(${argsStr})
|
|
1328
|
+
resolve(r)
|
|
1329
|
+
} catch(e) {
|
|
1330
|
+
reject(e)
|
|
1331
|
+
}
|
|
1332
|
+
})
|
|
1333
|
+
`);
|
|
1334
|
+
return { result };
|
|
1335
|
+
};
|
|
1336
|
+
appHandlers["App.callFunction"] = async (ctx, params) => {
|
|
1337
|
+
const fnDecl = params.functionDeclaration;
|
|
1338
|
+
const args = params.args || [];
|
|
1339
|
+
const argsStr = args.map((a) => JSON.stringify(a)).join(", ");
|
|
1340
|
+
const result = await evalInSim(ctx, `
|
|
1341
|
+
Promise.resolve((${fnDecl})(${argsStr}))
|
|
1342
|
+
`);
|
|
1343
|
+
return { result };
|
|
1344
|
+
};
|
|
1345
|
+
appHandlers["App.captureScreenshot"] = async (ctx) => {
|
|
1346
|
+
const sim = getSimulator(ctx);
|
|
1347
|
+
if (!sim) throw new Error("Simulator not connected");
|
|
1348
|
+
const img = await sim.capturePage();
|
|
1349
|
+
return { data: img.toPNG().toString("base64") };
|
|
1350
|
+
};
|
|
1351
|
+
appHandlers["App.enableLog"] = async () => ({});
|
|
1352
|
+
appHandlers["App.exit"] = async (ctx) => {
|
|
1353
|
+
await ctx.workspace.closeProject();
|
|
1354
|
+
return {};
|
|
1355
|
+
};
|
|
1356
|
+
appHandlers["App.mockWxMethod"] = async () => {
|
|
1357
|
+
throw new Error("mockWxMethod is not supported (requires AppService layer)");
|
|
1358
|
+
};
|
|
1359
|
+
appHandlers["App.addBinding"] = async () => {
|
|
1360
|
+
throw new Error("exposeFunction is not supported (requires AppService layer)");
|
|
1361
|
+
};
|
|
1362
|
+
|
|
1363
|
+
// src/main/services/automation/registry.ts
|
|
1364
|
+
var elementRegistry = /* @__PURE__ */ new Map();
|
|
1365
|
+
var nextElementId = 1;
|
|
1366
|
+
function registerElement(selector, index, pageId) {
|
|
1367
|
+
const id = `el_${nextElementId++}`;
|
|
1368
|
+
elementRegistry.set(id, { selector, index, pageId });
|
|
1369
|
+
return id;
|
|
1370
|
+
}
|
|
1371
|
+
function getElementRef(params) {
|
|
1372
|
+
const id = params.elementId;
|
|
1373
|
+
const ref = elementRegistry.get(id);
|
|
1374
|
+
if (!ref) throw new Error(`Element not found: ${id} (stale reference)`);
|
|
1375
|
+
return ref;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// src/main/services/automation/handlers/page.ts
|
|
1379
|
+
var pageHandlers = {};
|
|
1380
|
+
pageHandlers["Page.getElement"] = async (ctx, params) => {
|
|
1381
|
+
const selector = params.selector;
|
|
1382
|
+
const pageId = params.pageId || 1;
|
|
1383
|
+
const escaped = selector.replace(/'/g, "\\'");
|
|
1384
|
+
const info = await evalInSim(ctx, inIframe(`
|
|
1385
|
+
const el = _doc.querySelector('${escaped}')
|
|
1386
|
+
if (!el) return null
|
|
1387
|
+
return { tagName: el.tagName.toLowerCase() }
|
|
1388
|
+
`));
|
|
1389
|
+
if (!info) throw new Error(`Element not found: ${selector}`);
|
|
1390
|
+
const elementId = registerElement(selector, 0, pageId);
|
|
1391
|
+
return { elementId, tagName: info.tagName };
|
|
1392
|
+
};
|
|
1393
|
+
pageHandlers["Page.getElements"] = async (ctx, params) => {
|
|
1394
|
+
const selector = params.selector;
|
|
1395
|
+
const pageId = params.pageId || 1;
|
|
1396
|
+
const escaped = selector.replace(/'/g, "\\'");
|
|
1397
|
+
const items = await evalInSim(ctx, inIframe(`
|
|
1398
|
+
return Array.from(_doc.querySelectorAll('${escaped}')).map(el => ({
|
|
1399
|
+
tagName: el.tagName.toLowerCase(),
|
|
1400
|
+
}))
|
|
1401
|
+
`));
|
|
1402
|
+
return {
|
|
1403
|
+
elements: items.map((item, i) => ({
|
|
1404
|
+
elementId: registerElement(selector, i, pageId),
|
|
1405
|
+
tagName: item.tagName
|
|
1406
|
+
}))
|
|
1407
|
+
};
|
|
1408
|
+
};
|
|
1409
|
+
pageHandlers["Page.getData"] = async (ctx, params) => {
|
|
1410
|
+
const pathKey = params.path;
|
|
1411
|
+
const appdata = await evalInSim(
|
|
1412
|
+
ctx,
|
|
1413
|
+
`window.__simulatorData ? window.__simulatorData.getAppdata() : {}`
|
|
1414
|
+
);
|
|
1415
|
+
if (!pathKey) return { data: appdata };
|
|
1416
|
+
const keys = pathKey.replace(/\[(\d+)\]/g, ".$1").split(".");
|
|
1417
|
+
let val = appdata;
|
|
1418
|
+
for (const k of keys) {
|
|
1419
|
+
if (val == null || typeof val !== "object") {
|
|
1420
|
+
val = void 0;
|
|
1421
|
+
break;
|
|
1422
|
+
}
|
|
1423
|
+
val = val[k];
|
|
1424
|
+
}
|
|
1425
|
+
return { data: val };
|
|
1426
|
+
};
|
|
1427
|
+
pageHandlers["Page.setData"] = async () => {
|
|
1428
|
+
throw new Error("Page.setData is not supported (requires AppService layer)");
|
|
1429
|
+
};
|
|
1430
|
+
pageHandlers["Page.callMethod"] = async (_ctx, params) => {
|
|
1431
|
+
throw new Error(`Page.callMethod('${params.method}') is not supported (requires AppService layer)`);
|
|
1432
|
+
};
|
|
1433
|
+
pageHandlers["Page.getWindowProperties"] = async (ctx, params) => {
|
|
1434
|
+
const names = params.names;
|
|
1435
|
+
const properties = await evalInSim(ctx, inIframe(`
|
|
1436
|
+
return ${JSON.stringify(names)}.map(n => {
|
|
1437
|
+
try { return eval(n) } catch { return undefined }
|
|
1438
|
+
})
|
|
1439
|
+
`));
|
|
1440
|
+
return { properties };
|
|
1441
|
+
};
|
|
1442
|
+
|
|
1443
|
+
// src/main/services/automation/handlers/element.ts
|
|
1444
|
+
var elementHandlers = {};
|
|
1445
|
+
elementHandlers["Element.tap"] = async (ctx, params) => {
|
|
1446
|
+
const ref = getElementRef(params);
|
|
1447
|
+
await evalInSim(ctx, inIframe(`
|
|
1448
|
+
${buildElAccess(ref)}
|
|
1449
|
+
if (!el) throw new Error('Element not found')
|
|
1450
|
+
el.click()
|
|
1451
|
+
`));
|
|
1452
|
+
return {};
|
|
1453
|
+
};
|
|
1454
|
+
elementHandlers["Element.triggerEvent"] = async (ctx, params) => {
|
|
1455
|
+
const ref = getElementRef(params);
|
|
1456
|
+
const type = params.type;
|
|
1457
|
+
const detail = params.detail || {};
|
|
1458
|
+
await evalInSim(ctx, inIframe(`
|
|
1459
|
+
${buildElAccess(ref)}
|
|
1460
|
+
if (!el) throw new Error('Element not found')
|
|
1461
|
+
el.dispatchEvent(new CustomEvent('${type}', { detail: ${JSON.stringify(detail)}, bubbles: true }))
|
|
1462
|
+
`));
|
|
1463
|
+
return {};
|
|
1464
|
+
};
|
|
1465
|
+
elementHandlers["Element.touchstart"] = async (ctx, params) => {
|
|
1466
|
+
const ref = getElementRef(params);
|
|
1467
|
+
await evalInSim(ctx, inIframe(`
|
|
1468
|
+
${buildElAccess(ref)}
|
|
1469
|
+
if (el) el.dispatchEvent(new TouchEvent('touchstart', { bubbles: true }))
|
|
1470
|
+
`));
|
|
1471
|
+
return {};
|
|
1472
|
+
};
|
|
1473
|
+
elementHandlers["Element.touchmove"] = async (ctx, params) => {
|
|
1474
|
+
const ref = getElementRef(params);
|
|
1475
|
+
await evalInSim(ctx, inIframe(`
|
|
1476
|
+
${buildElAccess(ref)}
|
|
1477
|
+
if (el) el.dispatchEvent(new TouchEvent('touchmove', { bubbles: true }))
|
|
1478
|
+
`));
|
|
1479
|
+
return {};
|
|
1480
|
+
};
|
|
1481
|
+
elementHandlers["Element.touchend"] = async (ctx, params) => {
|
|
1482
|
+
const ref = getElementRef(params);
|
|
1483
|
+
await evalInSim(ctx, inIframe(`
|
|
1484
|
+
${buildElAccess(ref)}
|
|
1485
|
+
if (el) el.dispatchEvent(new TouchEvent('touchend', { bubbles: true }))
|
|
1486
|
+
`));
|
|
1487
|
+
return {};
|
|
1488
|
+
};
|
|
1489
|
+
elementHandlers["Element.getDOMProperties"] = async (ctx, params) => {
|
|
1490
|
+
const ref = getElementRef(params);
|
|
1491
|
+
const names = params.names;
|
|
1492
|
+
const properties = await evalInSim(ctx, inIframe(`
|
|
1493
|
+
${buildElAccess(ref)}
|
|
1494
|
+
if (!el) return ${JSON.stringify(names.map(() => null))}
|
|
1495
|
+
return ${JSON.stringify(names)}.map(n => el[n])
|
|
1496
|
+
`));
|
|
1497
|
+
return { properties };
|
|
1498
|
+
};
|
|
1499
|
+
elementHandlers["Element.getAttributes"] = async (ctx, params) => {
|
|
1500
|
+
const ref = getElementRef(params);
|
|
1501
|
+
const names = params.names;
|
|
1502
|
+
const attributes = await evalInSim(ctx, inIframe(`
|
|
1503
|
+
${buildElAccess(ref)}
|
|
1504
|
+
if (!el) return ${JSON.stringify(names.map(() => null))}
|
|
1505
|
+
return ${JSON.stringify(names)}.map(n => el.getAttribute(n))
|
|
1506
|
+
`));
|
|
1507
|
+
return { attributes };
|
|
1508
|
+
};
|
|
1509
|
+
elementHandlers["Element.getStyles"] = async (ctx, params) => {
|
|
1510
|
+
const ref = getElementRef(params);
|
|
1511
|
+
const names = params.names;
|
|
1512
|
+
const styles = await evalInSim(ctx, inIframe(`
|
|
1513
|
+
${buildElAccess(ref)}
|
|
1514
|
+
if (!el) return ${JSON.stringify(names.map(() => ""))}
|
|
1515
|
+
const cs = getComputedStyle(el)
|
|
1516
|
+
return ${JSON.stringify(names)}.map(n => cs.getPropertyValue(n))
|
|
1517
|
+
`));
|
|
1518
|
+
return { styles };
|
|
1519
|
+
};
|
|
1520
|
+
elementHandlers["Element.getWXML"] = async (ctx, params) => {
|
|
1521
|
+
const ref = getElementRef(params);
|
|
1522
|
+
const type = params.type || "inner";
|
|
1523
|
+
const prop = type === "outer" ? "outerHTML" : "innerHTML";
|
|
1524
|
+
const wxml = await evalInSim(ctx, inIframe(`
|
|
1525
|
+
${buildElAccess(ref)}
|
|
1526
|
+
return el ? el.${prop} : ''
|
|
1527
|
+
`));
|
|
1528
|
+
return { wxml };
|
|
1529
|
+
};
|
|
1530
|
+
elementHandlers["Element.getOffset"] = async (ctx, params) => {
|
|
1531
|
+
const ref = getElementRef(params);
|
|
1532
|
+
return evalInSim(ctx, inIframe(`
|
|
1533
|
+
${buildElAccess(ref)}
|
|
1534
|
+
if (!el) return { left: 0, top: 0 }
|
|
1535
|
+
const r = el.getBoundingClientRect()
|
|
1536
|
+
return { left: r.left, top: r.top }
|
|
1537
|
+
`));
|
|
1538
|
+
};
|
|
1539
|
+
elementHandlers["Element.getElement"] = async (ctx, params) => {
|
|
1540
|
+
const parentRef = getElementRef(params);
|
|
1541
|
+
const selector = params.selector;
|
|
1542
|
+
const escaped = selector.replace(/'/g, "\\'");
|
|
1543
|
+
const info = await evalInSim(ctx, inIframe(`
|
|
1544
|
+
${buildElAccess(parentRef, "parent")}
|
|
1545
|
+
if (!parent) return null
|
|
1546
|
+
const child = parent.querySelector('${escaped}')
|
|
1547
|
+
if (!child) return null
|
|
1548
|
+
return { tagName: child.tagName.toLowerCase() }
|
|
1549
|
+
`));
|
|
1550
|
+
if (!info) throw new Error(`Child element not found: ${selector}`);
|
|
1551
|
+
const combinedSelector = `${parentRef.selector} ${selector}`;
|
|
1552
|
+
const elementId = registerElement(combinedSelector, 0, parentRef.pageId);
|
|
1553
|
+
return { elementId, tagName: info.tagName };
|
|
1554
|
+
};
|
|
1555
|
+
elementHandlers["Element.getElements"] = async (ctx, params) => {
|
|
1556
|
+
const parentRef = getElementRef(params);
|
|
1557
|
+
const selector = params.selector;
|
|
1558
|
+
const escaped = selector.replace(/'/g, "\\'");
|
|
1559
|
+
const items = await evalInSim(ctx, inIframe(`
|
|
1560
|
+
${buildElAccess(parentRef, "parent")}
|
|
1561
|
+
if (!parent) return []
|
|
1562
|
+
return Array.from(parent.querySelectorAll('${escaped}')).map(el => ({
|
|
1563
|
+
tagName: el.tagName.toLowerCase(),
|
|
1564
|
+
}))
|
|
1565
|
+
`));
|
|
1566
|
+
const combinedSelector = `${parentRef.selector} ${selector}`;
|
|
1567
|
+
return {
|
|
1568
|
+
elements: items.map((item, i) => ({
|
|
1569
|
+
elementId: registerElement(combinedSelector, i, parentRef.pageId),
|
|
1570
|
+
tagName: item.tagName
|
|
1571
|
+
}))
|
|
1572
|
+
};
|
|
1573
|
+
};
|
|
1574
|
+
elementHandlers["Element.getProperties"] = async (ctx, params) => {
|
|
1575
|
+
return elementHandlers["Element.getDOMProperties"](ctx, params);
|
|
1576
|
+
};
|
|
1577
|
+
elementHandlers["Element.setData"] = async () => {
|
|
1578
|
+
throw new Error("Element.setData (component) is not supported");
|
|
1579
|
+
};
|
|
1580
|
+
elementHandlers["Element.getData"] = async () => {
|
|
1581
|
+
throw new Error("Element.getData (component) is not supported");
|
|
1582
|
+
};
|
|
1583
|
+
elementHandlers["Element.callMethod"] = async () => {
|
|
1584
|
+
throw new Error("Element.callMethod (component) is not supported");
|
|
1585
|
+
};
|
|
1586
|
+
elementHandlers["Element.callFunction"] = async () => {
|
|
1587
|
+
throw new Error("Element.callFunction is not supported");
|
|
1588
|
+
};
|
|
1589
|
+
|
|
1590
|
+
// src/main/services/automation/index.ts
|
|
1591
|
+
var handlers = {
|
|
1592
|
+
...toolHandlers,
|
|
1593
|
+
...appHandlers,
|
|
1594
|
+
...pageHandlers,
|
|
1595
|
+
...elementHandlers
|
|
1596
|
+
};
|
|
1597
|
+
var currentPort = null;
|
|
1598
|
+
var portIpcRegistered = false;
|
|
1599
|
+
async function startAutomationServer(ctx, port = 0) {
|
|
1600
|
+
const wss = new WebSocketServer({ port });
|
|
1601
|
+
const clients = /* @__PURE__ */ new Set();
|
|
1602
|
+
await new Promise((resolve, reject) => {
|
|
1603
|
+
wss.once("listening", () => resolve());
|
|
1604
|
+
wss.once("error", (err) => reject(err));
|
|
1605
|
+
});
|
|
1606
|
+
const addr = wss.address();
|
|
1607
|
+
const resolvedPort = typeof addr === "object" && addr ? addr.port : port;
|
|
1608
|
+
currentPort = resolvedPort;
|
|
1609
|
+
if (!portIpcRegistered) {
|
|
1610
|
+
portIpcRegistered = true;
|
|
1611
|
+
ipcMain9.handle(AutomationChannel.GetPort, () => currentPort);
|
|
1612
|
+
}
|
|
1613
|
+
function broadcast(event) {
|
|
1614
|
+
const msg = JSON.stringify(event);
|
|
1615
|
+
for (const ws of clients) {
|
|
1616
|
+
if (ws.readyState === ws.OPEN) ws.send(msg);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
let consoleForwardingSetup = false;
|
|
1620
|
+
function setupConsoleForwarding() {
|
|
1621
|
+
if (consoleForwardingSetup) return;
|
|
1622
|
+
consoleForwardingSetup = true;
|
|
1623
|
+
const check = setInterval(() => {
|
|
1624
|
+
const sim = getSimulator(ctx);
|
|
1625
|
+
if (!sim) return;
|
|
1626
|
+
clearInterval(check);
|
|
1627
|
+
sim.on("ipc-message-host", (_event, channel, data) => {
|
|
1628
|
+
if (channel === SimulatorChannel.Console) {
|
|
1629
|
+
const logData = data;
|
|
1630
|
+
broadcast({
|
|
1631
|
+
method: "App.logAdded",
|
|
1632
|
+
params: { type: logData.level || "log", args: logData.args || [] }
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
});
|
|
1636
|
+
}, 1e3);
|
|
1637
|
+
setTimeout(() => clearInterval(check), 3e4);
|
|
1638
|
+
}
|
|
1639
|
+
wss.on("connection", (ws) => {
|
|
1640
|
+
clients.add(ws);
|
|
1641
|
+
setupConsoleForwarding();
|
|
1642
|
+
ws.on("message", async (raw) => {
|
|
1643
|
+
let req;
|
|
1644
|
+
try {
|
|
1645
|
+
req = JSON.parse(String(raw));
|
|
1646
|
+
} catch {
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
const { id, method, params = {} } = req;
|
|
1650
|
+
const handler = handlers[method];
|
|
1651
|
+
let response;
|
|
1652
|
+
if (!handler) {
|
|
1653
|
+
response = { id, error: { message: `Unknown method: ${method}` } };
|
|
1654
|
+
} else {
|
|
1655
|
+
try {
|
|
1656
|
+
const result = await handler(ctx, params);
|
|
1657
|
+
response = { id, result };
|
|
1658
|
+
} catch (err) {
|
|
1659
|
+
response = { id, error: { message: err.message || String(err) } };
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(response));
|
|
1663
|
+
});
|
|
1664
|
+
ws.on("close", () => clients.delete(ws));
|
|
1665
|
+
});
|
|
1666
|
+
return {
|
|
1667
|
+
close: () => {
|
|
1668
|
+
if (currentPort === resolvedPort) currentPort = null;
|
|
1669
|
+
wss.close();
|
|
1670
|
+
},
|
|
1671
|
+
port: resolvedPort
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// src/main/services/mcp/server.ts
|
|
1676
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1677
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
1678
|
+
import { createServer } from "http";
|
|
1679
|
+
import { createRequire } from "node:module";
|
|
1680
|
+
|
|
1681
|
+
// src/main/services/mcp/target-manager.ts
|
|
1682
|
+
import CDP from "chrome-remote-interface";
|
|
1683
|
+
var SIMULATOR_URL_PATTERN = "localhost:7788";
|
|
1684
|
+
var MAX_BUFFER = 500;
|
|
1685
|
+
var RECONNECT_INTERVAL_MS = 3e3;
|
|
1686
|
+
var cdpPort = 9222;
|
|
1687
|
+
var targets = {
|
|
1688
|
+
simulator: { client: null, connected: false, timer: null, consoleLogs: [], networkRequests: [] },
|
|
1689
|
+
workbench: { client: null, connected: false, timer: null, consoleLogs: [], networkRequests: [] }
|
|
1690
|
+
};
|
|
1691
|
+
function setCdpPort(port) {
|
|
1692
|
+
cdpPort = port;
|
|
1693
|
+
}
|
|
1694
|
+
function getTargetState(kind) {
|
|
1695
|
+
return targets[kind];
|
|
1696
|
+
}
|
|
1697
|
+
async function listCdpTargets() {
|
|
1698
|
+
try {
|
|
1699
|
+
return await CDP.List({ port: cdpPort });
|
|
1700
|
+
} catch {
|
|
1701
|
+
return [];
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
function findTarget(allTargets, kind) {
|
|
1705
|
+
if (kind === "simulator") {
|
|
1706
|
+
return allTargets.find((t) => t.url?.includes(SIMULATOR_URL_PATTERN));
|
|
1707
|
+
}
|
|
1708
|
+
return allTargets.find(
|
|
1709
|
+
(t) => t.type === "page" && t.url?.includes("entries/main/index.html") && !t.url?.includes(SIMULATOR_URL_PATTERN)
|
|
1710
|
+
);
|
|
1711
|
+
}
|
|
1712
|
+
async function connectTarget(kind) {
|
|
1713
|
+
const state = targets[kind];
|
|
1714
|
+
if (state.client) {
|
|
1715
|
+
try {
|
|
1716
|
+
await state.client.close();
|
|
1717
|
+
} catch {
|
|
1718
|
+
}
|
|
1719
|
+
state.client = null;
|
|
1720
|
+
}
|
|
1721
|
+
const allTargets = await listCdpTargets();
|
|
1722
|
+
const target = findTarget(allTargets, kind);
|
|
1723
|
+
if (!target) {
|
|
1724
|
+
state.connected = false;
|
|
1725
|
+
scheduleReconnect(kind);
|
|
1726
|
+
return;
|
|
1727
|
+
}
|
|
1728
|
+
let client = null;
|
|
1729
|
+
try {
|
|
1730
|
+
client = await CDP({ port: cdpPort, target });
|
|
1731
|
+
await Promise.all([
|
|
1732
|
+
client.Page.enable(),
|
|
1733
|
+
client.Runtime.enable(),
|
|
1734
|
+
client.DOM.enable(),
|
|
1735
|
+
client.Network.enable(),
|
|
1736
|
+
client.Console.enable()
|
|
1737
|
+
]);
|
|
1738
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1739
|
+
const pushNetworkRequest = (entry) => {
|
|
1740
|
+
state.networkRequests.push(entry);
|
|
1741
|
+
if (state.networkRequests.length > MAX_BUFFER) state.networkRequests = state.networkRequests.slice(-MAX_BUFFER);
|
|
1742
|
+
};
|
|
1743
|
+
client.Runtime.on("consoleAPICalled", (params) => {
|
|
1744
|
+
const entry = {
|
|
1745
|
+
level: params.type,
|
|
1746
|
+
text: params.args.map((a) => String(a.value ?? a.description ?? JSON.stringify(a))).join(" "),
|
|
1747
|
+
timestamp: new Date(params.timestamp).toISOString()
|
|
1748
|
+
};
|
|
1749
|
+
state.consoleLogs.push(entry);
|
|
1750
|
+
if (state.consoleLogs.length > MAX_BUFFER) state.consoleLogs = state.consoleLogs.slice(-MAX_BUFFER);
|
|
1751
|
+
});
|
|
1752
|
+
client.Console.on("messageAdded", (params) => {
|
|
1753
|
+
const msg = params.message;
|
|
1754
|
+
state.consoleLogs.push({ level: msg.level, text: msg.text, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1755
|
+
if (state.consoleLogs.length > MAX_BUFFER) state.consoleLogs = state.consoleLogs.slice(-MAX_BUFFER);
|
|
1756
|
+
});
|
|
1757
|
+
client.Network.on("requestWillBeSent", (params) => {
|
|
1758
|
+
pending.set(params.requestId, { url: params.request.url, method: params.request.method, timestamp: params.timestamp });
|
|
1759
|
+
});
|
|
1760
|
+
client.Network.on("responseReceived", (params) => {
|
|
1761
|
+
const req = pending.get(params.requestId);
|
|
1762
|
+
if (!req) return;
|
|
1763
|
+
pending.delete(params.requestId);
|
|
1764
|
+
pushNetworkRequest({
|
|
1765
|
+
url: req.url,
|
|
1766
|
+
method: req.method,
|
|
1767
|
+
status: params.response.status,
|
|
1768
|
+
mimeType: params.response.mimeType,
|
|
1769
|
+
responseSize: params.response.encodedDataLength || 0,
|
|
1770
|
+
timing: params.response.timing ? { requestTime: params.response.timing.requestTime, receiveHeadersEnd: params.response.timing.receiveHeadersEnd } : null
|
|
1771
|
+
});
|
|
1772
|
+
});
|
|
1773
|
+
client.Network.on("loadingFailed", (params) => {
|
|
1774
|
+
const req = pending.get(params.requestId);
|
|
1775
|
+
if (!req) return;
|
|
1776
|
+
pending.delete(params.requestId);
|
|
1777
|
+
pushNetworkRequest({
|
|
1778
|
+
url: req.url,
|
|
1779
|
+
method: req.method,
|
|
1780
|
+
status: 0,
|
|
1781
|
+
mimeType: "",
|
|
1782
|
+
responseSize: 0,
|
|
1783
|
+
timing: null
|
|
1784
|
+
});
|
|
1785
|
+
});
|
|
1786
|
+
client.on("disconnect", () => {
|
|
1787
|
+
state.connected = false;
|
|
1788
|
+
state.client = null;
|
|
1789
|
+
scheduleReconnect(kind);
|
|
1790
|
+
});
|
|
1791
|
+
state.client = client;
|
|
1792
|
+
state.connected = true;
|
|
1793
|
+
if (state.timer) {
|
|
1794
|
+
clearTimeout(state.timer);
|
|
1795
|
+
state.timer = null;
|
|
1796
|
+
}
|
|
1797
|
+
} catch {
|
|
1798
|
+
if (client) {
|
|
1799
|
+
try {
|
|
1800
|
+
await client.close();
|
|
1801
|
+
} catch {
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
state.client = null;
|
|
1805
|
+
state.connected = false;
|
|
1806
|
+
scheduleReconnect(kind);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
function scheduleReconnect(kind) {
|
|
1810
|
+
const state = targets[kind];
|
|
1811
|
+
if (state.timer) return;
|
|
1812
|
+
state.timer = setTimeout(async () => {
|
|
1813
|
+
state.timer = null;
|
|
1814
|
+
await connectTarget(kind);
|
|
1815
|
+
}, RECONNECT_INTERVAL_MS);
|
|
1816
|
+
}
|
|
1817
|
+
function getClient(kind) {
|
|
1818
|
+
const state = targets[kind];
|
|
1819
|
+
if (!state.connected || !state.client) {
|
|
1820
|
+
const label = kind === "simulator" ? "\u6A21\u62DF\u5668" : "\u4E3B\u7A97\u53E3";
|
|
1821
|
+
throw new Error(`\u672A\u8FDE\u63A5\u5230${label}\u3002\u8BF7\u786E\u4FDD dimina-devtools \u6B63\u5728\u4EE5\u5F00\u53D1\u6A21\u5F0F\u8FD0\u884C\u3002`);
|
|
1822
|
+
}
|
|
1823
|
+
return state.client;
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
// src/main/services/mcp/tool-registry.ts
|
|
1827
|
+
import { z } from "zod";
|
|
1828
|
+
function registerCommonTargetTools(server, kind) {
|
|
1829
|
+
const descriptions = kind === "simulator" ? {
|
|
1830
|
+
screenshot: "Take a screenshot of the simulator webview",
|
|
1831
|
+
consoleLogs: "Get recent console output from the simulator (supports level/sinceTimestamp filters)",
|
|
1832
|
+
evaluate: "Execute JavaScript in the simulator context",
|
|
1833
|
+
getDom: "Get the DOM tree of the simulator page",
|
|
1834
|
+
networkLog: "Get recent network requests from the simulator (supports minStatus filter)"
|
|
1835
|
+
} : {
|
|
1836
|
+
screenshot: "Take a screenshot of the workbench main window",
|
|
1837
|
+
consoleLogs: "Get recent console output from the workbench main window (supports level/sinceTimestamp filters; warn/warning are treated as aliases)",
|
|
1838
|
+
evaluate: "Execute JavaScript in the workbench main window renderer",
|
|
1839
|
+
getDom: "Get the DOM tree of the workbench main window",
|
|
1840
|
+
networkLog: "Get recent network requests from the workbench main window (supports minStatus filter)"
|
|
1841
|
+
};
|
|
1842
|
+
server.tool(`${kind}_screenshot`, descriptions.screenshot, {}, async () => {
|
|
1843
|
+
const c = getClient(kind);
|
|
1844
|
+
const { data } = await c.Page.captureScreenshot({ format: "png" });
|
|
1845
|
+
return { content: [{ type: "image", data, mimeType: "image/png" }] };
|
|
1846
|
+
});
|
|
1847
|
+
server.tool(`${kind}_console_logs`, descriptions.consoleLogs, {
|
|
1848
|
+
limit: z.number().optional().default(50).describe("Maximum number of log entries"),
|
|
1849
|
+
level: z.enum(["error", "warning", "warn", "info", "log", "debug"]).optional().describe("Only return entries with this level"),
|
|
1850
|
+
sinceTimestamp: z.string().optional().describe("ISO timestamp; only return entries at or after this time")
|
|
1851
|
+
}, async ({ limit, level, sinceTimestamp }) => {
|
|
1852
|
+
getClient(kind);
|
|
1853
|
+
let entries = getTargetState(kind).consoleLogs;
|
|
1854
|
+
if (level) {
|
|
1855
|
+
const levels = level === "warning" || level === "warn" ? ["warning", "warn"] : [level];
|
|
1856
|
+
entries = entries.filter((e) => levels.includes(e.level));
|
|
1857
|
+
}
|
|
1858
|
+
if (sinceTimestamp) {
|
|
1859
|
+
entries = entries.filter((e) => e.timestamp >= sinceTimestamp);
|
|
1860
|
+
}
|
|
1861
|
+
return { content: [{ type: "text", text: JSON.stringify(entries.slice(-limit), null, 2) }] };
|
|
1862
|
+
});
|
|
1863
|
+
if (kind !== "workbench") {
|
|
1864
|
+
server.tool(`${kind}_evaluate`, descriptions.evaluate, {
|
|
1865
|
+
expression: z.string().describe("JavaScript expression to evaluate")
|
|
1866
|
+
}, async ({ expression }) => {
|
|
1867
|
+
const c = getClient(kind);
|
|
1868
|
+
const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
|
|
1869
|
+
if (result.exceptionDetails) {
|
|
1870
|
+
return { content: [{ type: "text", text: `Error: ${result.exceptionDetails.text}
|
|
1871
|
+
${JSON.stringify(result.exceptionDetails, null, 2)}` }], isError: true };
|
|
1872
|
+
}
|
|
1873
|
+
const value = result.result.value !== void 0 ? result.result.value : result.result.description || result.result.type;
|
|
1874
|
+
return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
server.tool(`${kind}_get_dom`, descriptions.getDom, {
|
|
1878
|
+
depth: z.number().optional().default(3).describe("Maximum depth")
|
|
1879
|
+
}, async ({ depth }) => {
|
|
1880
|
+
const c = getClient(kind);
|
|
1881
|
+
const { root } = await c.DOM.getDocument({ depth });
|
|
1882
|
+
return { content: [{ type: "text", text: JSON.stringify(root, null, 2) }] };
|
|
1883
|
+
});
|
|
1884
|
+
server.tool(`${kind}_network_log`, descriptions.networkLog, {
|
|
1885
|
+
limit: z.number().optional().default(20).describe("Maximum number of entries"),
|
|
1886
|
+
minStatus: z.number().optional().describe("Only return entries with status >= minStatus (status 0 = failed before response is included when minStatus >= 400)")
|
|
1887
|
+
}, async ({ limit, minStatus }) => {
|
|
1888
|
+
getClient(kind);
|
|
1889
|
+
let entries = getTargetState(kind).networkRequests;
|
|
1890
|
+
if (minStatus !== void 0) {
|
|
1891
|
+
entries = entries.filter((e) => e.status >= minStatus || e.status === 0 && minStatus >= 400);
|
|
1892
|
+
}
|
|
1893
|
+
return { content: [{ type: "text", text: JSON.stringify(entries.slice(-limit), null, 2) }] };
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
// src/main/services/mcp/tools/context-tools.ts
|
|
1898
|
+
var MAX_KEYS = 50;
|
|
1899
|
+
var MAX_HINT_TEXT = 200;
|
|
1900
|
+
var SIMULATOR_PROBE_EXPR = `(() => {
|
|
1901
|
+
const out = {
|
|
1902
|
+
url: location.href,
|
|
1903
|
+
title: document.title,
|
|
1904
|
+
viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
1905
|
+
currentRoute: null,
|
|
1906
|
+
pageStackDepth: 0,
|
|
1907
|
+
storageKeys: [],
|
|
1908
|
+
storageCount: 0,
|
|
1909
|
+
appDataKeys: [],
|
|
1910
|
+
bridgeReady: false,
|
|
1911
|
+
simulatorDataPresent: false,
|
|
1912
|
+
};
|
|
1913
|
+
try {
|
|
1914
|
+
const frames = document.querySelectorAll('.dimina-native-webview__window');
|
|
1915
|
+
out.pageStackDepth = frames.length;
|
|
1916
|
+
const active = frames[frames.length - 1];
|
|
1917
|
+
if (active) {
|
|
1918
|
+
out.currentRoute = active.getAttribute('data-route')
|
|
1919
|
+
|| active.getAttribute('src')
|
|
1920
|
+
|| active.getAttribute('data-path')
|
|
1921
|
+
|| null;
|
|
1922
|
+
}
|
|
1923
|
+
} catch (e) {}
|
|
1924
|
+
try {
|
|
1925
|
+
const bridge = window.__simulatorData;
|
|
1926
|
+
if (bridge) {
|
|
1927
|
+
out.simulatorDataPresent = true;
|
|
1928
|
+
if (typeof bridge.getStorageSnapshot === 'function') {
|
|
1929
|
+
const snap = bridge.getStorageSnapshot();
|
|
1930
|
+
out.bridgeReady = !!(snap && snap.ready);
|
|
1931
|
+
const data = (snap && snap.data) || {};
|
|
1932
|
+
out.storageKeys = Object.keys(data).slice(0, ${MAX_KEYS});
|
|
1933
|
+
out.storageCount = Object.keys(data).length;
|
|
1934
|
+
}
|
|
1935
|
+
if (typeof bridge.getAppdata === 'function') {
|
|
1936
|
+
const app = bridge.getAppdata() || {};
|
|
1937
|
+
out.appDataKeys = Object.keys(app).slice(0, ${MAX_KEYS});
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
} catch (e) {}
|
|
1941
|
+
return JSON.stringify(out);
|
|
1942
|
+
})()`;
|
|
1943
|
+
var WORKBENCH_PROBE_EXPR = `(() => JSON.stringify({
|
|
1944
|
+
url: location.href,
|
|
1945
|
+
title: document.title,
|
|
1946
|
+
viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
1947
|
+
}))()`;
|
|
1948
|
+
function summarizeConsole(kind) {
|
|
1949
|
+
const logs = getTargetState(kind).consoleLogs;
|
|
1950
|
+
let recentErrorCount = 0;
|
|
1951
|
+
let recentWarningCount = 0;
|
|
1952
|
+
let lastError = null;
|
|
1953
|
+
for (const entry of logs) {
|
|
1954
|
+
if (entry.level === "error") {
|
|
1955
|
+
recentErrorCount++;
|
|
1956
|
+
lastError = { level: entry.level, text: entry.text.slice(0, MAX_HINT_TEXT), timestamp: entry.timestamp };
|
|
1957
|
+
} else if (entry.level === "warning" || entry.level === "warn") {
|
|
1958
|
+
recentWarningCount++;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
return { total: logs.length, recentErrorCount, recentWarningCount, lastError };
|
|
1962
|
+
}
|
|
1963
|
+
function summarizeNetwork(kind) {
|
|
1964
|
+
const reqs = getTargetState(kind).networkRequests;
|
|
1965
|
+
let recentNetworkErrorCount = 0;
|
|
1966
|
+
let lastNetworkError = null;
|
|
1967
|
+
for (const r of reqs) {
|
|
1968
|
+
if (r.status >= 400 || r.status === 0) {
|
|
1969
|
+
recentNetworkErrorCount++;
|
|
1970
|
+
lastNetworkError = { url: r.url, status: r.status, method: r.method };
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
return { total: reqs.length, recentNetworkErrorCount, lastNetworkError };
|
|
1974
|
+
}
|
|
1975
|
+
function buildDisconnectedOverview(kind, hints) {
|
|
1976
|
+
if (kind === "simulator") {
|
|
1977
|
+
return {
|
|
1978
|
+
connected: false,
|
|
1979
|
+
url: "",
|
|
1980
|
+
title: "",
|
|
1981
|
+
viewport: null,
|
|
1982
|
+
currentRoute: null,
|
|
1983
|
+
pageStackDepth: 0,
|
|
1984
|
+
storageKeys: [],
|
|
1985
|
+
storageCount: 0,
|
|
1986
|
+
appDataKeys: [],
|
|
1987
|
+
recentErrorCount: 0,
|
|
1988
|
+
lastError: null,
|
|
1989
|
+
recentWarningCount: 0,
|
|
1990
|
+
recentNetworkErrorCount: 0,
|
|
1991
|
+
lastNetworkError: null,
|
|
1992
|
+
consoleLogTotal: 0,
|
|
1993
|
+
networkRequestTotal: 0,
|
|
1994
|
+
hints
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
return {
|
|
1998
|
+
connected: false,
|
|
1999
|
+
url: "",
|
|
2000
|
+
title: "",
|
|
2001
|
+
viewport: null,
|
|
2002
|
+
recentErrorCount: 0,
|
|
2003
|
+
lastError: null,
|
|
2004
|
+
recentNetworkErrorCount: 0,
|
|
2005
|
+
lastNetworkError: null,
|
|
2006
|
+
consoleLogTotal: 0,
|
|
2007
|
+
networkRequestTotal: 0,
|
|
2008
|
+
hints
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
function registerContextTools(server) {
|
|
2012
|
+
server.tool(
|
|
2013
|
+
"simulator_get_overview",
|
|
2014
|
+
"One-shot orientation snapshot of the simulator (page, storage keys, appdata keys, recent errors). Call at session start to avoid many probe queries.",
|
|
2015
|
+
{},
|
|
2016
|
+
async () => {
|
|
2017
|
+
const state = getTargetState("simulator");
|
|
2018
|
+
const hints = [];
|
|
2019
|
+
if (!state.connected || !state.client) {
|
|
2020
|
+
const payload2 = buildDisconnectedOverview("simulator", ["simulator not connected \u2014 ensure dimina-devtools is running in dev mode"]);
|
|
2021
|
+
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
2022
|
+
}
|
|
2023
|
+
let result;
|
|
2024
|
+
try {
|
|
2025
|
+
const c = getClient("simulator");
|
|
2026
|
+
result = await c.Runtime.evaluate({ expression: SIMULATOR_PROBE_EXPR, returnByValue: true });
|
|
2027
|
+
} catch {
|
|
2028
|
+
const payload2 = buildDisconnectedOverview("simulator", [...hints, "simulator disconnected before overview could finish"]);
|
|
2029
|
+
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
2030
|
+
}
|
|
2031
|
+
if (result.exceptionDetails) {
|
|
2032
|
+
return {
|
|
2033
|
+
content: [{ type: "text", text: `Error: ${result.exceptionDetails.text}` }],
|
|
2034
|
+
isError: true
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
let probe;
|
|
2038
|
+
try {
|
|
2039
|
+
probe = JSON.parse(result.result.value);
|
|
2040
|
+
} catch {
|
|
2041
|
+
return {
|
|
2042
|
+
content: [{ type: "text", text: "Error: probe returned non-JSON payload" }],
|
|
2043
|
+
isError: true
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
if (!probe.simulatorDataPresent) hints.push("simulator bridge not ready (window.__simulatorData missing)");
|
|
2047
|
+
else if (!probe.bridgeReady) hints.push("simulator bridge present but storage snapshot not ready");
|
|
2048
|
+
if (probe.pageStackDepth === 0) hints.push("no active miniapp page (empty page stack)");
|
|
2049
|
+
if (probe.storageCount > MAX_KEYS) hints.push(`storageKeys truncated to first ${MAX_KEYS} of ${probe.storageCount}`);
|
|
2050
|
+
const consoleSum = summarizeConsole("simulator");
|
|
2051
|
+
const networkSum = summarizeNetwork("simulator");
|
|
2052
|
+
const payload = {
|
|
2053
|
+
connected: true,
|
|
2054
|
+
url: probe.url,
|
|
2055
|
+
title: probe.title,
|
|
2056
|
+
viewport: probe.viewport,
|
|
2057
|
+
currentRoute: probe.currentRoute,
|
|
2058
|
+
pageStackDepth: probe.pageStackDepth,
|
|
2059
|
+
storageKeys: probe.storageKeys,
|
|
2060
|
+
storageCount: probe.storageCount,
|
|
2061
|
+
appDataKeys: probe.appDataKeys,
|
|
2062
|
+
recentErrorCount: consoleSum.recentErrorCount,
|
|
2063
|
+
lastError: consoleSum.lastError,
|
|
2064
|
+
recentWarningCount: consoleSum.recentWarningCount,
|
|
2065
|
+
recentNetworkErrorCount: networkSum.recentNetworkErrorCount,
|
|
2066
|
+
lastNetworkError: networkSum.lastNetworkError,
|
|
2067
|
+
consoleLogTotal: consoleSum.total,
|
|
2068
|
+
networkRequestTotal: networkSum.total,
|
|
2069
|
+
hints
|
|
2070
|
+
};
|
|
2071
|
+
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
2072
|
+
}
|
|
2073
|
+
);
|
|
2074
|
+
server.tool(
|
|
2075
|
+
"workbench_get_overview",
|
|
2076
|
+
"One-shot orientation snapshot of the workbench main window (url, title, viewport, recent errors, totals).",
|
|
2077
|
+
{},
|
|
2078
|
+
async () => {
|
|
2079
|
+
const state = getTargetState("workbench");
|
|
2080
|
+
const hints = [];
|
|
2081
|
+
if (!state.connected || !state.client) {
|
|
2082
|
+
const payload2 = buildDisconnectedOverview("workbench", ["workbench not connected \u2014 ensure dimina-devtools is running in dev mode"]);
|
|
2083
|
+
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
2084
|
+
}
|
|
2085
|
+
let result;
|
|
2086
|
+
try {
|
|
2087
|
+
const c = getClient("workbench");
|
|
2088
|
+
result = await c.Runtime.evaluate({ expression: WORKBENCH_PROBE_EXPR, returnByValue: true });
|
|
2089
|
+
} catch {
|
|
2090
|
+
const payload2 = buildDisconnectedOverview("workbench", [...hints, "workbench disconnected before overview could finish"]);
|
|
2091
|
+
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
2092
|
+
}
|
|
2093
|
+
if (result.exceptionDetails) {
|
|
2094
|
+
return {
|
|
2095
|
+
content: [{ type: "text", text: `Error: ${result.exceptionDetails.text}` }],
|
|
2096
|
+
isError: true
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
let probe;
|
|
2100
|
+
try {
|
|
2101
|
+
probe = JSON.parse(result.result.value);
|
|
2102
|
+
} catch {
|
|
2103
|
+
return {
|
|
2104
|
+
content: [{ type: "text", text: "Error: probe returned non-JSON payload" }],
|
|
2105
|
+
isError: true
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
const consoleSum = summarizeConsole("workbench");
|
|
2109
|
+
const networkSum = summarizeNetwork("workbench");
|
|
2110
|
+
const payload = {
|
|
2111
|
+
connected: true,
|
|
2112
|
+
url: probe.url,
|
|
2113
|
+
title: probe.title,
|
|
2114
|
+
viewport: probe.viewport,
|
|
2115
|
+
recentErrorCount: consoleSum.recentErrorCount,
|
|
2116
|
+
lastError: consoleSum.lastError,
|
|
2117
|
+
recentNetworkErrorCount: networkSum.recentNetworkErrorCount,
|
|
2118
|
+
lastNetworkError: networkSum.lastNetworkError,
|
|
2119
|
+
consoleLogTotal: consoleSum.total,
|
|
2120
|
+
networkRequestTotal: networkSum.total,
|
|
2121
|
+
hints
|
|
2122
|
+
};
|
|
2123
|
+
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
2124
|
+
}
|
|
2125
|
+
);
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
// src/main/services/mcp/tools/simulator-tools.ts
|
|
2129
|
+
import { z as z2 } from "zod";
|
|
2130
|
+
function registerSimulatorTools(server) {
|
|
2131
|
+
server.tool("simulator_navigate", "Navigate the simulator to a URL, or reload the current page", {
|
|
2132
|
+
url: z2.string().optional().describe("URL to navigate to"),
|
|
2133
|
+
reload: z2.boolean().optional().describe("If true, reload current page instead of navigating")
|
|
2134
|
+
}, async ({ url, reload }) => {
|
|
2135
|
+
const c = getClient("simulator");
|
|
2136
|
+
if (reload) {
|
|
2137
|
+
await c.Page.reload({ ignoreCache: false });
|
|
2138
|
+
const note = url ? " (url ignored because reload=true)" : "";
|
|
2139
|
+
return { content: [{ type: "text", text: `Reloaded simulator${note}` }] };
|
|
2140
|
+
}
|
|
2141
|
+
if (url) {
|
|
2142
|
+
const result = await c.Page.navigate({ url });
|
|
2143
|
+
return { content: [{ type: "text", text: JSON.stringify({ frameId: result.frameId, loaderId: result.loaderId }, null, 2) }] };
|
|
2144
|
+
}
|
|
2145
|
+
return { content: [{ type: "text", text: "Error: either url or reload is required" }], isError: true };
|
|
2146
|
+
});
|
|
2147
|
+
server.tool("simulator_input", "Dispatch input to the simulator. `tap_coord` requires x and y. `tap_selector` requires selector and optional nth. `type` requires selector and text. `scroll` requires x, y, deltaX, and deltaY. `key` is best-effort for common keys such as Enter, Escape, Tab, Arrow* and Space.", {
|
|
2148
|
+
action: z2.enum(["tap_coord", "tap_selector", "type", "scroll", "key"]).describe("Input action to dispatch"),
|
|
2149
|
+
x: z2.number().optional().describe("Viewport x coordinate for `tap_coord` or `scroll`"),
|
|
2150
|
+
y: z2.number().optional().describe("Viewport y coordinate for `tap_coord` or `scroll`"),
|
|
2151
|
+
selector: z2.string().optional().describe("CSS selector for `tap_selector` or `type`"),
|
|
2152
|
+
nth: z2.number().int().optional().describe("Zero-based index when selector matches multiple elements; defaults to 0"),
|
|
2153
|
+
text: z2.string().optional().describe("Text to insert for `type`"),
|
|
2154
|
+
key: z2.string().optional().describe("Best-effort keyboard key name for `key`; common keys such as Enter, Escape, Tab, Arrow* and Space work best"),
|
|
2155
|
+
deltaX: z2.number().optional().describe("Horizontal wheel delta for `scroll`"),
|
|
2156
|
+
deltaY: z2.number().optional().describe("Vertical wheel delta for `scroll`")
|
|
2157
|
+
}, async ({ action, x, y, selector, nth, text, key, deltaX, deltaY }) => {
|
|
2158
|
+
const c = getClient("simulator");
|
|
2159
|
+
const err = (msg) => ({ content: [{ type: "text", text: `Error: ${msg}` }], isError: true });
|
|
2160
|
+
const clickAt = async (cx, cy) => {
|
|
2161
|
+
await c.Input.dispatchMouseEvent({ type: "mousePressed", x: cx, y: cy, button: "left", clickCount: 1 });
|
|
2162
|
+
await c.Input.dispatchMouseEvent({ type: "mouseReleased", x: cx, y: cy, button: "left", clickCount: 1 });
|
|
2163
|
+
};
|
|
2164
|
+
if (action === "tap_coord") {
|
|
2165
|
+
if (typeof x !== "number" || typeof y !== "number") return err("tap_coord requires x and y");
|
|
2166
|
+
await clickAt(x, y);
|
|
2167
|
+
return { content: [{ type: "text", text: `Tapped at (${x}, ${y})` }] };
|
|
2168
|
+
}
|
|
2169
|
+
if (action === "tap_selector") {
|
|
2170
|
+
if (!selector) return err("tap_selector requires selector");
|
|
2171
|
+
const index = nth ?? 0;
|
|
2172
|
+
const expression = `(() => {
|
|
2173
|
+
const payload = ${JSON.stringify({ selector, index })}
|
|
2174
|
+
try {
|
|
2175
|
+
const matches = Array.from(document.querySelectorAll(payload.selector))
|
|
2176
|
+
if (matches.length === 0) {
|
|
2177
|
+
return { ok: false, reason: 'no_match', message: \`selector matched no elements: \${payload.selector}\` }
|
|
2178
|
+
}
|
|
2179
|
+
if (payload.index < 0 || payload.index >= matches.length) {
|
|
2180
|
+
return { ok: false, reason: 'out_of_range', message: \`nth \${payload.index} out of range (matches: \${matches.length})\` }
|
|
2181
|
+
}
|
|
2182
|
+
const element = matches[payload.index]
|
|
2183
|
+
element.scrollIntoView({ block: 'center', inline: 'center' })
|
|
2184
|
+
const rect = element.getBoundingClientRect()
|
|
2185
|
+
if (rect.width === 0 || rect.height === 0) {
|
|
2186
|
+
return { ok: false, reason: 'not_visible', message: \`selector matched an element, but it is not visible or rendered (zero rect): \${payload.selector}[\${payload.index}]\` }
|
|
2187
|
+
}
|
|
2188
|
+
return {
|
|
2189
|
+
ok: true,
|
|
2190
|
+
selector: payload.selector,
|
|
2191
|
+
index: payload.index,
|
|
2192
|
+
x: rect.left + rect.width / 2,
|
|
2193
|
+
y: rect.top + rect.height / 2,
|
|
2194
|
+
rect: { left: rect.left, top: rect.top, width: rect.width, height: rect.height },
|
|
2195
|
+
}
|
|
2196
|
+
} catch (error) {
|
|
2197
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
2198
|
+
return { ok: false, reason: 'selector_error', message: \`invalid selector: \${payload.selector} (\${message})\` }
|
|
2199
|
+
}
|
|
2200
|
+
})()`;
|
|
2201
|
+
const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
|
|
2202
|
+
const value = result.result?.value;
|
|
2203
|
+
if (!value) return err(`selector evaluation returned no result: ${selector}[${index}]`);
|
|
2204
|
+
if (!value.ok) return err(value.message);
|
|
2205
|
+
await clickAt(value.x, value.y);
|
|
2206
|
+
return {
|
|
2207
|
+
content: [{
|
|
2208
|
+
type: "text",
|
|
2209
|
+
text: `Tapped selector ${value.selector}[${value.index}] at (${value.x}, ${value.y}) within rect (${value.rect.left}, ${value.rect.top}, ${value.rect.width} x ${value.rect.height})`
|
|
2210
|
+
}]
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
if (action === "type") {
|
|
2214
|
+
if (!selector || typeof text !== "string") return err("type requires selector and text");
|
|
2215
|
+
const { root } = await c.DOM.getDocument({ depth: 0 });
|
|
2216
|
+
const { nodeIds } = await c.DOM.querySelectorAll({ nodeId: root.nodeId, selector });
|
|
2217
|
+
if (!nodeIds || nodeIds.length === 0) return err(`selector matched no elements: ${selector}`);
|
|
2218
|
+
const index = nth ?? 0;
|
|
2219
|
+
if (index < 0 || index >= nodeIds.length) return err(`nth ${index} out of range (matches: ${nodeIds.length})`);
|
|
2220
|
+
await c.DOM.focus({ nodeId: nodeIds[index] });
|
|
2221
|
+
try {
|
|
2222
|
+
await c.Input.insertText({ text });
|
|
2223
|
+
} catch {
|
|
2224
|
+
for (const ch of text) {
|
|
2225
|
+
await c.Input.dispatchKeyEvent({ type: "char", text: ch });
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
return { content: [{ type: "text", text: `Typed ${text.length} char(s) into ${selector}[${index}]` }] };
|
|
2229
|
+
}
|
|
2230
|
+
if (action === "scroll") {
|
|
2231
|
+
if (typeof x !== "number" || typeof y !== "number" || typeof deltaX !== "number" || typeof deltaY !== "number") {
|
|
2232
|
+
return err("scroll requires x, y, deltaX, deltaY");
|
|
2233
|
+
}
|
|
2234
|
+
await c.Input.dispatchMouseEvent({ type: "mouseWheel", x, y, deltaX, deltaY });
|
|
2235
|
+
return { content: [{ type: "text", text: `Scrolled at (${x}, ${y}) by (${deltaX}, ${deltaY})` }] };
|
|
2236
|
+
}
|
|
2237
|
+
if (action === "key") {
|
|
2238
|
+
if (!key) return err("key requires key");
|
|
2239
|
+
const isSingleChar = key.length === 1;
|
|
2240
|
+
const down = { type: "keyDown", key, code: key };
|
|
2241
|
+
if (isSingleChar) down.text = key;
|
|
2242
|
+
await c.Input.dispatchKeyEvent(down);
|
|
2243
|
+
await c.Input.dispatchKeyEvent({ type: "keyUp", key, code: key });
|
|
2244
|
+
return { content: [{ type: "text", text: `Dispatched key ${key}` }] };
|
|
2245
|
+
}
|
|
2246
|
+
return err(`unknown action: ${action}`);
|
|
2247
|
+
});
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
// src/main/services/mcp/tools/workbench-tools.ts
|
|
2251
|
+
function registerWorkbenchTools(server) {
|
|
2252
|
+
server.tool("workbench_list_targets", "List all available CDP targets (pages, webviews, etc.)", {}, async () => {
|
|
2253
|
+
const allTargets = await listCdpTargets();
|
|
2254
|
+
const summary = allTargets.map((t) => ({ id: t.id, type: t.type, title: t.title, url: t.url }));
|
|
2255
|
+
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
2256
|
+
});
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
// src/main/services/mcp/server.ts
|
|
2260
|
+
var require2 = createRequire(import.meta.url);
|
|
2261
|
+
var pkg = require2("../../../../package.json");
|
|
2262
|
+
function buildServer() {
|
|
2263
|
+
const server = new McpServer({
|
|
2264
|
+
name: "@dimina-kit/devtools",
|
|
2265
|
+
version: pkg.version
|
|
2266
|
+
});
|
|
2267
|
+
registerCommonTargetTools(server, "simulator");
|
|
2268
|
+
registerSimulatorTools(server);
|
|
2269
|
+
registerCommonTargetTools(server, "workbench");
|
|
2270
|
+
registerWorkbenchTools(server);
|
|
2271
|
+
registerContextTools(server);
|
|
2272
|
+
return server;
|
|
2273
|
+
}
|
|
2274
|
+
function startMcpServer(resolvedCdpPort, mcpPort) {
|
|
2275
|
+
setCdpPort(resolvedCdpPort);
|
|
2276
|
+
connectTarget("simulator").catch(() => {
|
|
2277
|
+
});
|
|
2278
|
+
connectTarget("workbench").catch(() => {
|
|
2279
|
+
});
|
|
2280
|
+
const server = buildServer();
|
|
2281
|
+
const transports = /* @__PURE__ */ new Map();
|
|
2282
|
+
const httpServer = createServer(async (req, res) => {
|
|
2283
|
+
const url = new URL(req.url ?? "/", `http://localhost`);
|
|
2284
|
+
if (req.method === "GET" && url.pathname === "/sse") {
|
|
2285
|
+
const transport = new SSEServerTransport("/message", res);
|
|
2286
|
+
transports.set(transport.sessionId, transport);
|
|
2287
|
+
res.on("close", () => transports.delete(transport.sessionId));
|
|
2288
|
+
await server.connect(transport);
|
|
2289
|
+
} else if (req.method === "POST" && url.pathname === "/message") {
|
|
2290
|
+
const sessionId = url.searchParams.get("sessionId") ?? "";
|
|
2291
|
+
const transport = transports.get(sessionId);
|
|
2292
|
+
if (transport) {
|
|
2293
|
+
await transport.handlePostMessage(req, res);
|
|
2294
|
+
} else {
|
|
2295
|
+
res.writeHead(404).end();
|
|
2296
|
+
}
|
|
2297
|
+
} else {
|
|
2298
|
+
res.writeHead(404).end();
|
|
2299
|
+
}
|
|
2300
|
+
});
|
|
2301
|
+
httpServer.on("error", (err) => {
|
|
2302
|
+
if (err.code === "EADDRINUSE") {
|
|
2303
|
+
console.warn(`[MCP] Port ${mcpPort} already in use \u2014 MCP server not started`);
|
|
2304
|
+
} else {
|
|
2305
|
+
console.error("[MCP] Server error:", err);
|
|
2306
|
+
}
|
|
2307
|
+
});
|
|
2308
|
+
httpServer.listen(mcpPort, "127.0.0.1", () => {
|
|
2309
|
+
console.log(`[MCP] SSE server listening on http://127.0.0.1:${mcpPort}/sse`);
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
// src/main/services/simulator-storage/index.ts
|
|
2314
|
+
import { app as app8, ipcMain as ipcMain10, webContents as wcStatic } from "electron";
|
|
2315
|
+
var attachedWc = null;
|
|
2316
|
+
var hostWc = null;
|
|
2317
|
+
function isSimulatorWebview(wc) {
|
|
2318
|
+
if (wc.isDestroyed()) return false;
|
|
2319
|
+
if (wc.getType() !== "webview") return false;
|
|
2320
|
+
return wc.getURL().includes("simulator.html");
|
|
2321
|
+
}
|
|
2322
|
+
function detach() {
|
|
2323
|
+
if (!attachedWc) return;
|
|
2324
|
+
const wc = attachedWc;
|
|
2325
|
+
attachedWc = null;
|
|
2326
|
+
try {
|
|
2327
|
+
if (!wc.isDestroyed() && wc.debugger.isAttached()) {
|
|
2328
|
+
wc.debugger.detach();
|
|
2329
|
+
}
|
|
2330
|
+
} catch {
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
async function attachToSim(wc) {
|
|
2334
|
+
if (attachedWc === wc) return;
|
|
2335
|
+
if (wc.isDestroyed()) return;
|
|
2336
|
+
detach();
|
|
2337
|
+
try {
|
|
2338
|
+
if (!wc.debugger.isAttached()) {
|
|
2339
|
+
wc.debugger.attach("1.3");
|
|
2340
|
+
}
|
|
2341
|
+
await wc.debugger.sendCommand("DOMStorage.enable");
|
|
2342
|
+
wc.debugger.on("message", onCdpMessage);
|
|
2343
|
+
wc.debugger.on("detach", () => {
|
|
2344
|
+
if (attachedWc === wc) attachedWc = null;
|
|
2345
|
+
});
|
|
2346
|
+
wc.once("destroyed", () => {
|
|
2347
|
+
if (attachedWc === wc) attachedWc = null;
|
|
2348
|
+
});
|
|
2349
|
+
attachedWc = wc;
|
|
2350
|
+
} catch (e) {
|
|
2351
|
+
console.warn("[storage-watcher] attach failed:", e.message);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
function onCdpMessage(_event, method, params) {
|
|
2355
|
+
if (!hostWc || hostWc.isDestroyed()) return;
|
|
2356
|
+
const p = params;
|
|
2357
|
+
let evt;
|
|
2358
|
+
switch (method) {
|
|
2359
|
+
case "DOMStorage.domStorageItemAdded":
|
|
2360
|
+
evt = { type: "added", key: String(p.key), newValue: String(p.newValue ?? "") };
|
|
2361
|
+
break;
|
|
2362
|
+
case "DOMStorage.domStorageItemUpdated":
|
|
2363
|
+
evt = {
|
|
2364
|
+
type: "updated",
|
|
2365
|
+
key: String(p.key),
|
|
2366
|
+
oldValue: String(p.oldValue ?? ""),
|
|
2367
|
+
newValue: String(p.newValue ?? "")
|
|
2368
|
+
};
|
|
2369
|
+
break;
|
|
2370
|
+
case "DOMStorage.domStorageItemRemoved":
|
|
2371
|
+
evt = { type: "removed", key: String(p.key) };
|
|
2372
|
+
break;
|
|
2373
|
+
case "DOMStorage.domStorageItemsCleared":
|
|
2374
|
+
evt = { type: "cleared" };
|
|
2375
|
+
break;
|
|
2376
|
+
default:
|
|
2377
|
+
return;
|
|
2378
|
+
}
|
|
2379
|
+
hostWc.send(SimulatorStorageChannel.Event, evt);
|
|
2380
|
+
}
|
|
2381
|
+
async function getSnapshot() {
|
|
2382
|
+
if (!attachedWc || attachedWc.isDestroyed()) return [];
|
|
2383
|
+
try {
|
|
2384
|
+
const origin = await attachedWc.executeJavaScript("location.origin");
|
|
2385
|
+
const result = await attachedWc.debugger.sendCommand("DOMStorage.getDOMStorageItems", {
|
|
2386
|
+
storageId: { securityOrigin: origin, isLocalStorage: true }
|
|
2387
|
+
});
|
|
2388
|
+
return result.entries.map(([key, value]) => ({ key, value }));
|
|
2389
|
+
} catch {
|
|
2390
|
+
return [];
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
function findSimulatorWebContents() {
|
|
2394
|
+
for (const wc of wcStatic.getAllWebContents()) {
|
|
2395
|
+
if (isSimulatorWebview(wc)) return wc;
|
|
2396
|
+
}
|
|
2397
|
+
return null;
|
|
2398
|
+
}
|
|
2399
|
+
async function inspectElement(sid) {
|
|
2400
|
+
if (!sid) return null;
|
|
2401
|
+
const sim = findSimulatorWebContents();
|
|
2402
|
+
if (!sim) return null;
|
|
2403
|
+
try {
|
|
2404
|
+
const result = await sim.executeJavaScript(
|
|
2405
|
+
`window.__simulatorData && window.__simulatorData.highlightElement ? window.__simulatorData.highlightElement(${JSON.stringify(sid)}) : null`
|
|
2406
|
+
);
|
|
2407
|
+
return result ?? null;
|
|
2408
|
+
} catch (e) {
|
|
2409
|
+
console.warn("[simulator-element] inspect failed:", e.message);
|
|
2410
|
+
return null;
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
async function clearElementInspection() {
|
|
2414
|
+
const sim = findSimulatorWebContents();
|
|
2415
|
+
if (!sim) return;
|
|
2416
|
+
try {
|
|
2417
|
+
await sim.executeJavaScript(
|
|
2418
|
+
"window.__simulatorData && window.__simulatorData.unhighlightElement && window.__simulatorData.unhighlightElement()"
|
|
2419
|
+
);
|
|
2420
|
+
} catch {
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
function setupSimulatorStorage(host) {
|
|
2424
|
+
hostWc = host;
|
|
2425
|
+
for (const wc of wcStatic.getAllWebContents()) {
|
|
2426
|
+
if (isSimulatorWebview(wc)) {
|
|
2427
|
+
void attachToSim(wc);
|
|
2428
|
+
break;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
app8.on("web-contents-created", (_event, wc) => {
|
|
2432
|
+
wc.on("did-finish-load", () => {
|
|
2433
|
+
if (isSimulatorWebview(wc)) {
|
|
2434
|
+
void attachToSim(wc);
|
|
2435
|
+
}
|
|
2436
|
+
});
|
|
2437
|
+
wc.on("did-navigate-in-page", () => {
|
|
2438
|
+
});
|
|
2439
|
+
});
|
|
2440
|
+
ipcMain10.handle(SimulatorStorageChannel.GetSnapshot, getSnapshot);
|
|
2441
|
+
ipcMain10.handle(SimulatorElementChannel.Inspect, (_event, sid) => inspectElement(sid));
|
|
2442
|
+
ipcMain10.handle(SimulatorElementChannel.Clear, clearElementInspection);
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
// src/main/services/update/update-manager.ts
|
|
2446
|
+
import { app as app9, ipcMain as ipcMain11, shell } from "electron";
|
|
2447
|
+
var UpdateManager = class {
|
|
2448
|
+
checker;
|
|
2449
|
+
mainWindow;
|
|
2450
|
+
getCurrentVersion;
|
|
2451
|
+
latestUpdate = null;
|
|
2452
|
+
downloadedPath = null;
|
|
2453
|
+
timer = null;
|
|
2454
|
+
initialTimer = null;
|
|
2455
|
+
constructor(opts) {
|
|
2456
|
+
this.checker = opts.checker;
|
|
2457
|
+
this.mainWindow = opts.mainWindow;
|
|
2458
|
+
this.getCurrentVersion = opts.getCurrentVersion ?? (() => app9.getVersion());
|
|
2459
|
+
this.registerIpc();
|
|
2460
|
+
this.startPeriodicCheck(opts.checkInterval ?? 60 * 60 * 1e3, opts.initialDelay ?? 5e3);
|
|
2461
|
+
}
|
|
2462
|
+
registerIpc() {
|
|
2463
|
+
ipcMain11.handle("updates:check", async () => {
|
|
2464
|
+
return this.check();
|
|
2465
|
+
});
|
|
2466
|
+
ipcMain11.handle("updates:download", async () => {
|
|
2467
|
+
return this.download();
|
|
2468
|
+
});
|
|
2469
|
+
ipcMain11.handle("updates:install", async () => {
|
|
2470
|
+
this.install();
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
async check() {
|
|
2474
|
+
try {
|
|
2475
|
+
const currentVersion = this.getCurrentVersion();
|
|
2476
|
+
const info = await this.checker.checkForUpdates(currentVersion);
|
|
2477
|
+
if (info) {
|
|
2478
|
+
this.latestUpdate = info;
|
|
2479
|
+
this.downloadedPath = null;
|
|
2480
|
+
return { hasUpdate: true, info };
|
|
2481
|
+
}
|
|
2482
|
+
return { hasUpdate: false };
|
|
2483
|
+
} catch (err) {
|
|
2484
|
+
console.warn("[UpdateManager] check failed:", err);
|
|
2485
|
+
return { hasUpdate: false };
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
async download() {
|
|
2489
|
+
if (!this.latestUpdate) {
|
|
2490
|
+
return { success: false, error: "No update available" };
|
|
2491
|
+
}
|
|
2492
|
+
try {
|
|
2493
|
+
const filePath = await this.checker.downloadUpdate(this.latestUpdate, (percent) => {
|
|
2494
|
+
this.mainWindow.webContents.send("updates:downloadProgress", { percent });
|
|
2495
|
+
});
|
|
2496
|
+
this.downloadedPath = filePath;
|
|
2497
|
+
return { success: true, filePath };
|
|
2498
|
+
} catch (err) {
|
|
2499
|
+
console.warn("[UpdateManager] download failed:", err);
|
|
2500
|
+
return { success: false, error: String(err) };
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
install() {
|
|
2504
|
+
if (this.downloadedPath) {
|
|
2505
|
+
shell.openPath(this.downloadedPath);
|
|
2506
|
+
app9.quit();
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
dispose() {
|
|
2510
|
+
if (this.timer) {
|
|
2511
|
+
clearInterval(this.timer);
|
|
2512
|
+
this.timer = null;
|
|
2513
|
+
}
|
|
2514
|
+
if (this.initialTimer) {
|
|
2515
|
+
clearTimeout(this.initialTimer);
|
|
2516
|
+
this.initialTimer = null;
|
|
2517
|
+
}
|
|
2518
|
+
ipcMain11.removeHandler("updates:check");
|
|
2519
|
+
ipcMain11.removeHandler("updates:download");
|
|
2520
|
+
ipcMain11.removeHandler("updates:install");
|
|
2521
|
+
}
|
|
2522
|
+
startPeriodicCheck(interval, initialDelay) {
|
|
2523
|
+
this.initialTimer = setTimeout(() => void this.checkAndNotify(), initialDelay);
|
|
2524
|
+
this.timer = setInterval(() => void this.checkAndNotify(), interval);
|
|
2525
|
+
}
|
|
2526
|
+
async checkAndNotify() {
|
|
2527
|
+
const result = await this.check();
|
|
2528
|
+
if (result.hasUpdate && result.info) {
|
|
2529
|
+
this.mainWindow.webContents.send("updates:available", result.info);
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
};
|
|
2533
|
+
|
|
2534
|
+
// src/main/services/update/github-release-checker.ts
|
|
2535
|
+
import { app as app10 } from "electron";
|
|
2536
|
+
|
|
2537
|
+
// src/main/app/app.ts
|
|
2538
|
+
var DEFAULT_MODULES = {
|
|
2539
|
+
projects: true,
|
|
2540
|
+
session: true,
|
|
2541
|
+
simulator: true,
|
|
2542
|
+
popover: true,
|
|
2543
|
+
settings: true
|
|
2544
|
+
};
|
|
2545
|
+
var MODULE_REGISTRARS = {
|
|
2546
|
+
projects: registerProjectsIpc,
|
|
2547
|
+
session: registerSessionIpc,
|
|
2548
|
+
simulator: (ctx) => {
|
|
2549
|
+
registerSimulatorIpc(ctx);
|
|
2550
|
+
registerPanelsIpc(ctx);
|
|
2551
|
+
registerToolbarIpc(ctx);
|
|
2552
|
+
},
|
|
2553
|
+
popover: registerPopoverIpc,
|
|
2554
|
+
settings: registerSettingsIpc
|
|
2555
|
+
};
|
|
2556
|
+
function parseAutoArgs() {
|
|
2557
|
+
const argv = process.argv;
|
|
2558
|
+
let auto = false;
|
|
2559
|
+
let autoPort = 9420;
|
|
2560
|
+
let projectPath = "";
|
|
2561
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2562
|
+
if (argv[i] === "auto" || argv[i] === "--auto") auto = true;
|
|
2563
|
+
if ((argv[i] === "--auto-port" || argv[i] === "--auto_port") && argv[i + 1]) {
|
|
2564
|
+
const parsed = parseInt(argv[i + 1], 10);
|
|
2565
|
+
if (Number.isFinite(parsed) && parsed >= 0) autoPort = parsed;
|
|
2566
|
+
}
|
|
2567
|
+
if (argv[i] === "--project" && argv[i + 1]) {
|
|
2568
|
+
projectPath = argv[i + 1];
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
return { auto, autoPort, projectPath };
|
|
2572
|
+
}
|
|
2573
|
+
function resolveModules(config) {
|
|
2574
|
+
return {
|
|
2575
|
+
...DEFAULT_MODULES,
|
|
2576
|
+
...config.modules
|
|
2577
|
+
};
|
|
2578
|
+
}
|
|
2579
|
+
async function disposeContext(ctx, updateManager) {
|
|
2580
|
+
updateManager?.dispose();
|
|
2581
|
+
await ctx.workspace.closeProject();
|
|
2582
|
+
}
|
|
2583
|
+
function createConfiguredMainWindow(config, rendererDir2) {
|
|
2584
|
+
const mainWindow = createMainWindow({
|
|
2585
|
+
title: config.appName ?? "Dimina DevTools",
|
|
2586
|
+
indexHtml: path6.join(rendererDir2, "entries/main/index.html"),
|
|
2587
|
+
width: config.window?.width,
|
|
2588
|
+
height: config.window?.height,
|
|
2589
|
+
minWidth: config.window?.minWidth,
|
|
2590
|
+
minHeight: config.window?.minHeight
|
|
2591
|
+
});
|
|
2592
|
+
if (config.icon) {
|
|
2593
|
+
const icon = nativeImage.createFromPath(config.icon);
|
|
2594
|
+
if (!icon.isEmpty()) mainWindow.setIcon(icon);
|
|
2595
|
+
}
|
|
2596
|
+
return mainWindow;
|
|
2597
|
+
}
|
|
2598
|
+
function createContext(config, mainWindow, rendererDir2) {
|
|
2599
|
+
return createWorkbenchContext({
|
|
2600
|
+
mainWindow,
|
|
2601
|
+
adapter: config.adapter,
|
|
2602
|
+
preloadPath: config.preloadPath ?? defaultPreloadPath,
|
|
2603
|
+
rendererDir: rendererDir2,
|
|
2604
|
+
panels: config.panels,
|
|
2605
|
+
appName: config.appName,
|
|
2606
|
+
apiNamespaces: config.apiNamespaces,
|
|
2607
|
+
brandingProvider: config.brandingProvider,
|
|
2608
|
+
toolbarActions: config.toolbarActions
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2611
|
+
function registerBuiltinModules(config, context) {
|
|
2612
|
+
const modules = resolveModules(config);
|
|
2613
|
+
Object.keys(modules).forEach((moduleId) => {
|
|
2614
|
+
if (modules[moduleId]) MODULE_REGISTRARS[moduleId](context);
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
function installMenu(config, mainWindow, context) {
|
|
2618
|
+
if (config.menuBuilder) {
|
|
2619
|
+
config.menuBuilder(mainWindow, context);
|
|
2620
|
+
} else {
|
|
2621
|
+
installAppMenu(context);
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
async function setupAutomation(instance) {
|
|
2625
|
+
const autoArgs = parseAutoArgs();
|
|
2626
|
+
if (autoArgs.auto) {
|
|
2627
|
+
const server = await startAutomationServer(instance.context, autoArgs.autoPort);
|
|
2628
|
+
instance.automationServer = server;
|
|
2629
|
+
console.log(`[automation] listening on ws://127.0.0.1:${server.port}`);
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
function setupMcp() {
|
|
2633
|
+
const settings = loadWorkbenchSettings();
|
|
2634
|
+
if (!settings.mcp.enabled) return;
|
|
2635
|
+
const cdpPortSwitch = app11.commandLine.getSwitchValue("remote-debugging-port");
|
|
2636
|
+
const cdpPort2 = cdpPortSwitch ? parseInt(cdpPortSwitch, 10) : settings.cdp.port;
|
|
2637
|
+
startMcpServer(cdpPort2, settings.mcp.port);
|
|
2638
|
+
}
|
|
2639
|
+
function wireAppWindowEvents(config, instance) {
|
|
2640
|
+
const { mainWindow, context } = instance;
|
|
2641
|
+
wireMainWindowEvents(mainWindow, {
|
|
2642
|
+
context,
|
|
2643
|
+
onResize: () => context.views.repositionAll(),
|
|
2644
|
+
onClose: async (e) => {
|
|
2645
|
+
if (!context.workspace.hasActiveSession()) return;
|
|
2646
|
+
e.preventDefault();
|
|
2647
|
+
if (config.onBeforeClose) {
|
|
2648
|
+
await config.onBeforeClose(instance);
|
|
2649
|
+
}
|
|
2650
|
+
await disposeContext(context);
|
|
2651
|
+
context.notify.windowNavigateBack();
|
|
2652
|
+
}
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
function enableDevRendererAutoReload(rendererDir2) {
|
|
2656
|
+
if (!app11.isPackaged) {
|
|
2657
|
+
let reloadTimer = null;
|
|
2658
|
+
fs5.watch(rendererDir2, { recursive: true }, () => {
|
|
2659
|
+
if (reloadTimer) clearTimeout(reloadTimer);
|
|
2660
|
+
reloadTimer = setTimeout(() => {
|
|
2661
|
+
for (const win of BrowserWindow3.getAllWindows()) {
|
|
2662
|
+
win.webContents.reload();
|
|
2663
|
+
}
|
|
2664
|
+
}, 300);
|
|
2665
|
+
});
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
function createWorkbenchApp(config = {}) {
|
|
2669
|
+
let setupPromise = null;
|
|
2670
|
+
let appEventsRegistered = false;
|
|
2671
|
+
setupCdpPort();
|
|
2672
|
+
async function setup() {
|
|
2673
|
+
if (setupPromise) return setupPromise;
|
|
2674
|
+
setupPromise = app11.whenReady().then(async () => {
|
|
2675
|
+
applyTheme(loadWorkbenchSettings().theme);
|
|
2676
|
+
const rendererDir2 = config.rendererDir ?? rendererDir;
|
|
2677
|
+
const mainWindow = createConfiguredMainWindow(config, rendererDir2);
|
|
2678
|
+
const context = createContext(config, mainWindow, rendererDir2);
|
|
2679
|
+
registerAppIpc(context);
|
|
2680
|
+
registerBuiltinModules(config, context);
|
|
2681
|
+
installMenu(config, mainWindow, context);
|
|
2682
|
+
const instance = {
|
|
2683
|
+
mainWindow,
|
|
2684
|
+
context,
|
|
2685
|
+
dispose: () => disposeContext(context, instance.updateManager)
|
|
2686
|
+
};
|
|
2687
|
+
if (config.onSetup) {
|
|
2688
|
+
await config.onSetup(instance);
|
|
2689
|
+
}
|
|
2690
|
+
if (config.updateChecker) {
|
|
2691
|
+
instance.updateManager = new UpdateManager({
|
|
2692
|
+
checker: config.updateChecker,
|
|
2693
|
+
mainWindow,
|
|
2694
|
+
checkInterval: config.updateOptions?.checkInterval,
|
|
2695
|
+
initialDelay: config.updateOptions?.initialDelay,
|
|
2696
|
+
getCurrentVersion: config.updateOptions?.getCurrentVersion
|
|
2697
|
+
});
|
|
2698
|
+
}
|
|
2699
|
+
await setupAutomation(instance);
|
|
2700
|
+
setupMcp();
|
|
2701
|
+
setupSimulatorStorage(mainWindow.webContents);
|
|
2702
|
+
wireAppWindowEvents(config, instance);
|
|
2703
|
+
enableDevRendererAutoReload(rendererDir2);
|
|
2704
|
+
return instance;
|
|
2705
|
+
});
|
|
2706
|
+
return setupPromise;
|
|
2707
|
+
}
|
|
2708
|
+
function start() {
|
|
2709
|
+
void setup();
|
|
2710
|
+
if (!appEventsRegistered) {
|
|
2711
|
+
appEventsRegistered = true;
|
|
2712
|
+
registerAppLifecycle();
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
return {
|
|
2716
|
+
setup,
|
|
2717
|
+
start
|
|
2718
|
+
};
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
// src/main/windows/settings-window/create.ts
|
|
2722
|
+
import { BrowserWindow as BrowserWindow4 } from "electron";
|
|
2723
|
+
import path7 from "path";
|
|
2724
|
+
async function createSettingsWindow(parent, rendererDir2) {
|
|
2725
|
+
const win = new BrowserWindow4({
|
|
2726
|
+
width: 420,
|
|
2727
|
+
height: 560,
|
|
2728
|
+
minWidth: 380,
|
|
2729
|
+
minHeight: 480,
|
|
2730
|
+
parent: parent ?? void 0,
|
|
2731
|
+
title: "\u5F00\u53D1\u5DE5\u5177\u8BBE\u7F6E",
|
|
2732
|
+
backgroundColor: themeBg(),
|
|
2733
|
+
webPreferences: { nodeIntegration: true, contextIsolation: false }
|
|
2734
|
+
});
|
|
2735
|
+
await win.loadFile(path7.join(rendererDir2, "entries/workbench-settings/index.html"));
|
|
2736
|
+
return win;
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
// src/main/windows/settings-window/events.ts
|
|
2740
|
+
function wireSettingsWindowEvents(win, onClosed) {
|
|
2741
|
+
win.on("closed", () => {
|
|
2742
|
+
onClosed();
|
|
2743
|
+
});
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
// src/main/app/launch.ts
|
|
2747
|
+
function launch(config = {}) {
|
|
2748
|
+
createWorkbenchApp(config).start();
|
|
2749
|
+
}
|
|
2750
|
+
async function openSettingsWindow(ctx) {
|
|
2751
|
+
if (!ctx.workbenchSettingsWindow || ctx.workbenchSettingsWindow.isDestroyed()) {
|
|
2752
|
+
ctx.workbenchSettingsWindow = await createSettingsWindow(ctx.mainWindow, ctx.rendererDir);
|
|
2753
|
+
wireSettingsWindowEvents(ctx.workbenchSettingsWindow, () => {
|
|
2754
|
+
ctx.workbenchSettingsWindow = null;
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
ctx.workbenchSettingsWindow.show();
|
|
2758
|
+
ctx.workbenchSettingsWindow.focus();
|
|
2759
|
+
ctx.notify.workbenchSettingsInit(ctx.workbenchSettingsWindow, {
|
|
2760
|
+
settings: loadWorkbenchSettings()
|
|
2761
|
+
});
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
// src/main/index.ts
|
|
2765
|
+
launch();
|