@dimina-kit/devtools 0.3.1 → 0.3.2-dev.20260522085907
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/README.md +297 -133
- package/dist/main/api.d.ts +6 -2
- package/dist/main/api.js +5 -2
- package/dist/main/app/app.d.ts +1 -1
- package/dist/main/app/app.js +84 -36
- package/dist/main/app/bootstrap.js +2 -1
- package/dist/main/app/launch.d.ts +2 -2
- package/dist/main/app/launch.js +10 -8
- package/dist/main/index.bundle.js +1567 -477
- package/dist/main/ipc/app.d.ts +2 -1
- package/dist/main/ipc/app.js +5 -4
- package/dist/main/ipc/index.d.ts +5 -4
- package/dist/main/ipc/index.js +5 -4
- package/dist/main/ipc/panels.d.ts +2 -1
- package/dist/main/ipc/panels.js +16 -13
- package/dist/main/ipc/popover.d.ts +4 -1
- package/dist/main/ipc/popover.js +15 -5
- package/dist/main/ipc/projects.d.ts +6 -1
- package/dist/main/ipc/projects.js +127 -6
- package/dist/main/ipc/session.d.ts +4 -1
- package/dist/main/ipc/session.js +34 -6
- package/dist/main/ipc/settings.d.ts +4 -1
- package/dist/main/ipc/settings.js +51 -23
- package/dist/main/ipc/simulator-module.d.ts +8 -0
- package/dist/main/ipc/simulator-module.js +19 -0
- package/dist/main/ipc/simulator.d.ts +2 -1
- package/dist/main/ipc/simulator.js +23 -18
- package/dist/main/ipc/toolbar.d.ts +2 -1
- package/dist/main/ipc/toolbar.js +3 -2
- package/dist/main/menu/index.js +11 -0
- package/dist/main/services/automation/exec.d.ts +2 -0
- package/dist/main/services/automation/exec.js +7 -9
- package/dist/main/services/automation/handlers/app.js +37 -51
- package/dist/main/services/automation/handlers/element.js +27 -41
- package/dist/main/services/automation/index.js +88 -13
- package/dist/main/services/default-adapter.js +3 -3
- package/dist/main/services/mcp/index.d.ts +1 -0
- package/dist/main/services/mcp/index.js +1 -0
- package/dist/main/services/mcp/server.d.ts +2 -1
- package/dist/main/services/mcp/server.js +10 -0
- package/dist/main/services/mcp/status.d.ts +10 -0
- package/dist/main/services/mcp/status.js +14 -0
- package/dist/main/services/mcp/target-manager.js +2 -1
- package/dist/main/services/module.d.ts +18 -0
- package/dist/main/services/module.js +2 -0
- package/dist/main/services/notifications/renderer-notifier.d.ts +5 -3
- package/dist/main/services/notifications/renderer-notifier.js +3 -6
- package/dist/main/services/projects/builtin-templates.d.ts +3 -0
- package/dist/main/services/projects/builtin-templates.js +24 -0
- package/dist/main/services/projects/create-project-service.d.ts +7 -0
- package/dist/main/services/projects/create-project-service.js +84 -0
- package/dist/main/services/projects/index.d.ts +7 -0
- package/dist/main/services/projects/index.js +9 -0
- package/dist/main/services/projects/local-provider.d.ts +3 -0
- package/dist/main/services/projects/local-provider.js +25 -0
- package/dist/main/services/projects/project-repository.d.ts +1 -1
- package/dist/main/services/projects/project-repository.js +3 -0
- package/dist/main/services/projects/templates.d.ts +21 -0
- package/dist/main/services/projects/templates.js +39 -0
- package/dist/main/services/projects/thumbnail.d.ts +8 -0
- package/dist/main/services/projects/thumbnail.js +37 -0
- package/dist/main/services/projects/types.d.ts +118 -0
- package/dist/main/services/projects/types.js +19 -0
- package/dist/main/services/settings/index.d.ts +11 -0
- package/dist/main/services/settings/index.js +18 -2
- package/dist/main/services/simulator/custom-apis.d.ts +10 -0
- package/dist/main/services/simulator/custom-apis.js +26 -0
- package/dist/main/services/simulator-storage/index.d.ts +22 -1
- package/dist/main/services/simulator-storage/index.js +334 -97
- package/dist/main/services/update/update-manager.d.ts +16 -1
- package/dist/main/services/update/update-manager.js +18 -15
- package/dist/main/services/views/view-manager.d.ts +7 -7
- package/dist/main/services/views/view-manager.js +53 -29
- package/dist/main/services/window-service.d.ts +24 -0
- package/dist/main/services/window-service.js +26 -0
- package/dist/main/services/workbench-context.d.ts +49 -2
- package/dist/main/services/workbench-context.js +15 -2
- package/dist/main/services/workspace/workspace-service.d.ts +9 -6
- package/dist/main/services/workspace/workspace-service.js +71 -11
- package/dist/main/simulator-apis.d.ts +23 -0
- package/dist/main/simulator-apis.js +24 -0
- package/dist/main/utils/disposable.d.ts +13 -0
- package/dist/main/utils/disposable.js +60 -0
- package/dist/main/utils/ipc-registry.d.ts +39 -0
- package/dist/main/utils/ipc-registry.js +109 -0
- package/dist/main/utils/ipc-schema.d.ts +28 -0
- package/dist/main/utils/ipc-schema.js +38 -0
- package/dist/main/utils/paths.d.ts +6 -0
- package/dist/main/utils/paths.js +6 -0
- package/dist/main/utils/sender-policy.d.ts +25 -0
- package/dist/main/utils/sender-policy.js +44 -0
- package/dist/main/utils/theme.d.ts +24 -0
- package/dist/main/utils/theme.js +38 -1
- package/dist/main/windows/main-window/create.d.ts +6 -0
- package/dist/main/windows/main-window/create.js +66 -10
- package/dist/main/windows/main-window/events.d.ts +2 -1
- package/dist/main/windows/main-window/events.js +25 -2
- package/dist/main/windows/navigation-hardening.d.ts +24 -0
- package/dist/main/windows/navigation-hardening.js +55 -0
- package/dist/main/windows/settings-window/create.js +11 -1
- package/dist/preload/index.d.ts +22 -0
- package/dist/preload/index.js +19 -0
- package/dist/preload/instrumentation/app-data.d.ts +32 -2
- package/dist/preload/instrumentation/app-data.js +253 -79
- package/dist/preload/instrumentation/index.d.ts +2 -2
- package/dist/preload/instrumentation/index.js +2 -2
- package/dist/preload/instrumentation/wxml.d.ts +16 -4
- package/dist/preload/instrumentation/wxml.js +188 -178
- package/dist/preload/miniapp-snapshot/host.d.ts +12 -0
- package/dist/preload/miniapp-snapshot/host.js +117 -0
- package/dist/preload/miniapp-snapshot/types.d.ts +62 -0
- package/dist/preload/miniapp-snapshot/types.js +15 -0
- package/dist/preload/runtime/bridge.js +5 -24
- package/dist/preload/runtime/custom-apis.d.ts +6 -0
- package/dist/preload/runtime/custom-apis.js +43 -0
- package/dist/preload/runtime/host.d.ts +1 -0
- package/dist/preload/runtime/host.js +13 -0
- package/dist/preload/shared/expose.d.ts +15 -0
- package/dist/preload/shared/expose.js +33 -0
- package/dist/preload/shared/page-iframe.d.ts +3 -0
- package/dist/preload/shared/page-iframe.js +6 -0
- package/dist/preload/shared/types.d.ts +2 -0
- package/dist/preload/windows/main.cjs +47 -0
- package/dist/preload/windows/main.cjs.map +7 -0
- package/dist/preload/windows/simulator.js +501 -272
- package/dist/renderer/assets/constants-DI382mTP.js +2 -0
- package/dist/renderer/assets/index-D8VFYBh-.js +46 -0
- package/dist/renderer/assets/{input-DxfYnzX_.js → input-CoWcfa8u.js} +2 -2
- package/dist/renderer/assets/ipc-transport-Cd2xPrsa.js +9 -0
- package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +1 -0
- package/dist/renderer/assets/popover-Nq5gT9DO.js +2 -0
- package/dist/renderer/assets/select-By5KH-UG.js +2 -0
- package/dist/renderer/assets/{settings-DteGraEU.js → settings-Dj_N70qR.js} +2 -2
- package/dist/renderer/assets/settings-api-Do0_Smq1.js +2 -0
- package/dist/renderer/assets/workbenchSettings-CazSLGYt.js +8 -0
- package/dist/renderer/entries/main/index.html +13 -5
- package/dist/renderer/entries/popover/index.html +8 -5
- package/dist/renderer/entries/settings/index.html +7 -5
- package/dist/renderer/entries/workbench-settings/index.html +7 -4
- package/dist/shared/ipc-channels.d.ts +46 -13
- package/dist/shared/ipc-channels.js +60 -14
- package/dist/shared/ipc-schemas.d.ts +118 -0
- package/dist/shared/ipc-schemas.js +133 -0
- package/dist/shared/simulator-route.d.ts +72 -0
- package/dist/shared/simulator-route.js +169 -0
- package/dist/shared/types.d.ts +82 -8
- package/dist/simulator/assets/simulator-DJCozrU5.js +9 -0
- package/dist/simulator/simulator.html +6 -43
- package/package.json +18 -29
- package/templates/blank/app.js +6 -0
- package/templates/blank/app.json +11 -0
- package/templates/blank/app.wxss +5 -0
- package/templates/blank/pages/index/index.js +6 -0
- package/templates/blank/pages/index/index.json +3 -0
- package/templates/blank/pages/index/index.wxml +3 -0
- package/templates/blank/pages/index/index.wxss +11 -0
- package/templates/blank/project.config.json +13 -0
- package/templates/blank/sitemap.json +9 -0
- package/templates/taro-todo/app.js +1 -0
- package/templates/taro-todo/app.json +1 -0
- package/templates/taro-todo/app.wxss +1 -0
- package/templates/taro-todo/base.wxml +851 -0
- package/templates/taro-todo/common.js +1 -0
- package/templates/taro-todo/comp.js +1 -0
- package/templates/taro-todo/comp.json +1 -0
- package/templates/taro-todo/comp.wxml +2 -0
- package/templates/taro-todo/custom-wrapper.js +1 -0
- package/templates/taro-todo/custom-wrapper.json +1 -0
- package/templates/taro-todo/custom-wrapper.wxml +4 -0
- package/templates/taro-todo/pages/index/index.js +2 -0
- package/templates/taro-todo/pages/index/index.json +1 -0
- package/templates/taro-todo/pages/index/index.wxml +2 -0
- package/templates/taro-todo/pages/index/index.wxss +1 -0
- package/templates/taro-todo/project.config.json +44 -0
- package/templates/taro-todo/runtime.js +1 -0
- package/templates/taro-todo/taro.js +2 -0
- package/templates/taro-todo/utils.wxs +39 -0
- package/templates/taro-todo/vendors.js +2 -0
- package/dist/preload/instrumentation/disposable.d.ts +0 -11
- package/dist/preload/instrumentation/disposable.js +0 -15
- package/dist/preload/windows/main.d.ts +0 -6
- package/dist/preload/windows/main.js +0 -6
- package/dist/renderer/assets/index-D1p6crtf.js +0 -46
- package/dist/renderer/assets/ipc-transport-ClIvDcYY.js +0 -9
- package/dist/renderer/assets/ipc-transport-EiWxu_hG.css +0 -1
- package/dist/renderer/assets/popover-CtXcBJtM.js +0 -2
- package/dist/renderer/assets/select-DMfp49f1.js +0 -2
- package/dist/renderer/assets/settings-api-CY84UMpN.js +0 -2
- package/dist/renderer/assets/workbenchSettings-DjELLnSJ.js +0 -8
- package/dist/simulator/assets/simulator-C1eHXnuq.js +0 -9
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// src/main/app/bootstrap.ts
|
|
2
2
|
import { app as app2 } from "electron";
|
|
3
3
|
|
|
4
|
+
// src/shared/constants.ts
|
|
5
|
+
var DEFAULT_CDP_PORT = 9222;
|
|
6
|
+
var DEFAULT_SCENE = 1001;
|
|
7
|
+
|
|
4
8
|
// src/main/services/settings/index.ts
|
|
5
9
|
import { app, nativeTheme } from "electron";
|
|
6
10
|
import fs from "fs";
|
|
@@ -8,13 +12,17 @@ import path from "path";
|
|
|
8
12
|
var DEFAULTS = {
|
|
9
13
|
cdp: {
|
|
10
14
|
enabled: false,
|
|
11
|
-
port:
|
|
15
|
+
port: DEFAULT_CDP_PORT
|
|
12
16
|
},
|
|
13
17
|
mcp: {
|
|
14
18
|
enabled: false,
|
|
15
19
|
port: 7789
|
|
16
20
|
},
|
|
17
|
-
|
|
21
|
+
compile: {
|
|
22
|
+
watch: true
|
|
23
|
+
},
|
|
24
|
+
theme: "system",
|
|
25
|
+
lastCreateBaseDir: null
|
|
18
26
|
};
|
|
19
27
|
function getSettingsFile() {
|
|
20
28
|
return path.join(app.getPath("userData"), "dimina-workbench-settings.json");
|
|
@@ -32,10 +40,19 @@ function loadWorkbenchSettings() {
|
|
|
32
40
|
enabled: data.mcp?.enabled ?? DEFAULTS.mcp.enabled,
|
|
33
41
|
port: data.mcp?.port ?? DEFAULTS.mcp.port
|
|
34
42
|
},
|
|
35
|
-
|
|
43
|
+
compile: {
|
|
44
|
+
watch: data.compile?.watch ?? DEFAULTS.compile.watch
|
|
45
|
+
},
|
|
46
|
+
theme,
|
|
47
|
+
lastCreateBaseDir: typeof data.lastCreateBaseDir === "string" ? data.lastCreateBaseDir : DEFAULTS.lastCreateBaseDir
|
|
36
48
|
};
|
|
37
49
|
} catch {
|
|
38
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
...DEFAULTS,
|
|
52
|
+
cdp: { ...DEFAULTS.cdp },
|
|
53
|
+
mcp: { ...DEFAULTS.mcp },
|
|
54
|
+
compile: { ...DEFAULTS.compile }
|
|
55
|
+
};
|
|
39
56
|
}
|
|
40
57
|
}
|
|
41
58
|
function saveWorkbenchSettings(settings) {
|
|
@@ -57,14 +74,14 @@ function setupCdpPort() {
|
|
|
57
74
|
if (settings.cdp.enabled || settings.mcp.enabled) {
|
|
58
75
|
app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port));
|
|
59
76
|
} else if (!app2.isPackaged) {
|
|
60
|
-
app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port ||
|
|
77
|
+
app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port || DEFAULT_CDP_PORT));
|
|
61
78
|
}
|
|
62
79
|
}
|
|
63
80
|
|
|
64
81
|
// src/main/app/app.ts
|
|
65
|
-
import { app as
|
|
66
|
-
import
|
|
67
|
-
import
|
|
82
|
+
import { app as app13, BrowserWindow as BrowserWindow4, nativeImage } from "electron";
|
|
83
|
+
import fs7 from "fs";
|
|
84
|
+
import path12 from "path";
|
|
68
85
|
|
|
69
86
|
// src/main/utils/paths.ts
|
|
70
87
|
import fs2 from "fs";
|
|
@@ -87,8 +104,86 @@ function resolveDevtoolsPackageRoot() {
|
|
|
87
104
|
var devtoolsPackageRoot = resolveDevtoolsPackageRoot();
|
|
88
105
|
var rendererDir = path2.join(devtoolsPackageRoot, "dist/renderer");
|
|
89
106
|
var defaultPreloadPath = path2.join(devtoolsPackageRoot, "dist/preload/windows/simulator.js");
|
|
107
|
+
var mainPreloadPath = path2.join(devtoolsPackageRoot, "dist/preload/windows/main.cjs");
|
|
90
108
|
var simulatorDir = path2.join(devtoolsPackageRoot, "dist/simulator");
|
|
91
109
|
|
|
110
|
+
// src/main/utils/theme.ts
|
|
111
|
+
import { BrowserWindow, nativeTheme as nativeTheme2 } from "electron";
|
|
112
|
+
|
|
113
|
+
// src/main/utils/disposable.ts
|
|
114
|
+
function toDisposable(fn) {
|
|
115
|
+
let done = false;
|
|
116
|
+
return {
|
|
117
|
+
dispose() {
|
|
118
|
+
if (done) return;
|
|
119
|
+
done = true;
|
|
120
|
+
return fn();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
var DisposableRegistry = class {
|
|
125
|
+
entries = [];
|
|
126
|
+
_disposed = false;
|
|
127
|
+
add(d) {
|
|
128
|
+
if (this._disposed) {
|
|
129
|
+
throw new Error("cannot add to disposed registry");
|
|
130
|
+
}
|
|
131
|
+
const fn = typeof d === "function" ? d : () => d.dispose();
|
|
132
|
+
const entry = { fn, released: false };
|
|
133
|
+
this.entries.push(entry);
|
|
134
|
+
return {
|
|
135
|
+
dispose: () => {
|
|
136
|
+
if (entry.released) return;
|
|
137
|
+
entry.released = true;
|
|
138
|
+
const i = this.entries.indexOf(entry);
|
|
139
|
+
if (i >= 0) this.entries.splice(i, 1);
|
|
140
|
+
return fn();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
async disposeAll() {
|
|
145
|
+
if (this._disposed) return;
|
|
146
|
+
this._disposed = true;
|
|
147
|
+
const items = this.entries.slice().reverse();
|
|
148
|
+
this.entries = [];
|
|
149
|
+
const errors = [];
|
|
150
|
+
for (const entry of items) {
|
|
151
|
+
if (entry.released) continue;
|
|
152
|
+
entry.released = true;
|
|
153
|
+
try {
|
|
154
|
+
await entry.fn();
|
|
155
|
+
} catch (e) {
|
|
156
|
+
errors.push(e);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (errors.length > 0) {
|
|
160
|
+
throw new AggregateError(errors, "DisposableRegistry encountered errors during disposeAll");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
dispose() {
|
|
164
|
+
return this.disposeAll();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// src/main/utils/theme.ts
|
|
169
|
+
function themeBg() {
|
|
170
|
+
return nativeTheme2.shouldUseDarkColors ? "#1a1a1a" : "#fafafa";
|
|
171
|
+
}
|
|
172
|
+
function installThemeBackgroundSync() {
|
|
173
|
+
const apply = () => {
|
|
174
|
+
const bg = themeBg();
|
|
175
|
+
for (const win of BrowserWindow.getAllWindows()) {
|
|
176
|
+
if (!win.isDestroyed()) {
|
|
177
|
+
win.setBackgroundColor(bg);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
nativeTheme2.on("updated", apply);
|
|
182
|
+
return toDisposable(() => {
|
|
183
|
+
nativeTheme2.removeListener("updated", apply);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
92
187
|
// src/main/app/lifecycle.ts
|
|
93
188
|
import { app as app3, globalShortcut } from "electron";
|
|
94
189
|
function registerAppLifecycle() {
|
|
@@ -102,7 +197,8 @@ function registerAppLifecycle() {
|
|
|
102
197
|
}
|
|
103
198
|
|
|
104
199
|
// src/main/windows/main-window/create.ts
|
|
105
|
-
import { app as app4, BrowserWindow, View,
|
|
200
|
+
import { app as app4, BrowserWindow as BrowserWindow2, View, session, shell as shell2 } from "electron";
|
|
201
|
+
import path4 from "path";
|
|
106
202
|
|
|
107
203
|
// src/main/services/simulator/referer.ts
|
|
108
204
|
var DEFAULT_VERSION = "develop";
|
|
@@ -123,18 +219,51 @@ function getSimulatorServicewechatReferer() {
|
|
|
123
219
|
return cachedRefererUrl;
|
|
124
220
|
}
|
|
125
221
|
|
|
126
|
-
// src/main/
|
|
127
|
-
import {
|
|
128
|
-
|
|
129
|
-
|
|
222
|
+
// src/main/windows/navigation-hardening.ts
|
|
223
|
+
import { shell } from "electron";
|
|
224
|
+
import path3 from "path";
|
|
225
|
+
import { pathToFileURL } from "url";
|
|
226
|
+
function handleWindowOpenExternal(url) {
|
|
227
|
+
try {
|
|
228
|
+
const u = new URL(url);
|
|
229
|
+
if (u.protocol === "http:" || u.protocol === "https:") {
|
|
230
|
+
void shell.openExternal(url);
|
|
231
|
+
}
|
|
232
|
+
} catch {
|
|
233
|
+
}
|
|
234
|
+
return { action: "deny" };
|
|
235
|
+
}
|
|
236
|
+
function applyNavigationHardening(wc, rendererDir2) {
|
|
237
|
+
const rendererPrefix = pathToFileURL(rendererDir2 + path3.sep).href;
|
|
238
|
+
wc.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
|
|
239
|
+
wc.on("will-navigate", (event, url) => {
|
|
240
|
+
let parsed;
|
|
241
|
+
try {
|
|
242
|
+
parsed = new URL(url);
|
|
243
|
+
} catch {
|
|
244
|
+
event.preventDefault();
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (parsed.protocol === "file:" && parsed.href.startsWith(rendererPrefix)) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
event.preventDefault();
|
|
251
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
252
|
+
void shell.openExternal(url);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
130
255
|
}
|
|
131
256
|
|
|
132
257
|
// src/main/windows/main-window/create.ts
|
|
133
258
|
var simulatorSessionConfigured = false;
|
|
134
|
-
function configureSimulatorSession() {
|
|
259
|
+
function configureSimulatorSession(simulatorPreloadPath) {
|
|
135
260
|
if (simulatorSessionConfigured) return;
|
|
136
261
|
simulatorSessionConfigured = true;
|
|
137
262
|
const simulatorSession = session.fromPartition("persist:simulator");
|
|
263
|
+
simulatorSession.registerPreloadScript({
|
|
264
|
+
type: "frame",
|
|
265
|
+
filePath: simulatorPreloadPath
|
|
266
|
+
});
|
|
138
267
|
simulatorSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
|
139
268
|
const forcedReferer = getSimulatorServicewechatReferer();
|
|
140
269
|
if (forcedReferer) {
|
|
@@ -148,13 +277,13 @@ function configureSimulatorSession() {
|
|
|
148
277
|
headers["access-control-allow-headers"] = ["*"];
|
|
149
278
|
headers["access-control-allow-methods"] = ["*"];
|
|
150
279
|
headers["Cross-Origin-Opener-Policy"] = ["same-origin"];
|
|
151
|
-
headers["Cross-Origin-Embedder-Policy"] = ["
|
|
280
|
+
headers["Cross-Origin-Embedder-Policy"] = ["credentialless"];
|
|
152
281
|
callback({ responseHeaders: headers });
|
|
153
282
|
});
|
|
154
283
|
}
|
|
155
284
|
function createMainWindow(opts) {
|
|
156
|
-
configureSimulatorSession();
|
|
157
|
-
const mainWindow = new
|
|
285
|
+
configureSimulatorSession(opts.simulatorPreloadPath);
|
|
286
|
+
const mainWindow = new BrowserWindow2({
|
|
158
287
|
width: opts.width ?? 1280,
|
|
159
288
|
height: opts.height ?? 980,
|
|
160
289
|
minWidth: opts.minWidth ?? 1e3,
|
|
@@ -163,8 +292,13 @@ function createMainWindow(opts) {
|
|
|
163
292
|
show: false,
|
|
164
293
|
backgroundColor: themeBg(),
|
|
165
294
|
webPreferences: {
|
|
166
|
-
nodeIntegration:
|
|
167
|
-
contextIsolation:
|
|
295
|
+
nodeIntegration: false,
|
|
296
|
+
contextIsolation: true,
|
|
297
|
+
// `sandbox: false` is required so the preload bundle can `require('electron')`
|
|
298
|
+
// to access contextBridge/ipcRenderer. The renderer itself runs isolated.
|
|
299
|
+
sandbox: false,
|
|
300
|
+
preload: mainPreloadPath,
|
|
301
|
+
// The simulator <webview> still relies on the webview tag; keep it enabled.
|
|
168
302
|
webviewTag: true
|
|
169
303
|
}
|
|
170
304
|
});
|
|
@@ -178,22 +312,45 @@ function createMainWindow(opts) {
|
|
|
178
312
|
}
|
|
179
313
|
}
|
|
180
314
|
});
|
|
181
|
-
|
|
315
|
+
const rendererDir2 = path4.dirname(opts.indexHtml);
|
|
316
|
+
applyNavigationHardening(mainWindow.webContents, rendererDir2);
|
|
317
|
+
mainWindow.webContents.on("will-attach-webview", (event, webPreferences, params) => {
|
|
318
|
+
;
|
|
319
|
+
webPreferences.partition = "persist:simulator";
|
|
320
|
+
params.partition = "persist:simulator";
|
|
182
321
|
webPreferences.contextIsolation = false;
|
|
322
|
+
void event;
|
|
323
|
+
});
|
|
324
|
+
mainWindow.webContents.on("did-attach-webview", (_event, webviewWebContents) => {
|
|
325
|
+
webviewWebContents.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
|
|
326
|
+
webviewWebContents.on("will-navigate", (e, url) => {
|
|
327
|
+
try {
|
|
328
|
+
const u = new URL(url);
|
|
329
|
+
if (u.protocol === "about:") return;
|
|
330
|
+
if ((u.protocol === "http:" || u.protocol === "https:") && (u.hostname === "localhost" || u.hostname === "127.0.0.1")) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
e.preventDefault();
|
|
334
|
+
if (u.protocol === "http:" || u.protocol === "https:") {
|
|
335
|
+
void shell2.openExternal(url);
|
|
336
|
+
}
|
|
337
|
+
} catch {
|
|
338
|
+
e.preventDefault();
|
|
339
|
+
}
|
|
340
|
+
});
|
|
183
341
|
});
|
|
184
342
|
mainWindow.loadFile(opts.indexHtml);
|
|
185
343
|
const container = new View();
|
|
186
344
|
const mainWebView = mainWindow.contentView;
|
|
187
345
|
container.addChildView(mainWebView);
|
|
188
346
|
mainWindow.contentView = container;
|
|
189
|
-
globalShortcut2.register("CommandOrControl+Shift+I", () => {
|
|
190
|
-
mainWindow.webContents.openDevTools({ mode: "detach" });
|
|
191
|
-
});
|
|
192
347
|
return mainWindow;
|
|
193
348
|
}
|
|
194
349
|
|
|
195
350
|
// src/main/windows/main-window/events.ts
|
|
351
|
+
import { globalShortcut as globalShortcut2 } from "electron";
|
|
196
352
|
function wireMainWindowEvents(win, state = {}) {
|
|
353
|
+
const registry = new DisposableRegistry();
|
|
197
354
|
const mainWebView = win.contentView.children[0];
|
|
198
355
|
const resizeMainWebView = () => {
|
|
199
356
|
if (mainWebView) {
|
|
@@ -204,25 +361,56 @@ function wireMainWindowEvents(win, state = {}) {
|
|
|
204
361
|
};
|
|
205
362
|
resizeMainWebView();
|
|
206
363
|
win.on("resize", resizeMainWebView);
|
|
364
|
+
registry.add(toDisposable(() => {
|
|
365
|
+
win.removeListener("resize", resizeMainWebView);
|
|
366
|
+
}));
|
|
207
367
|
if (state.onClose) {
|
|
208
|
-
|
|
368
|
+
const onCloseHandler = (event) => {
|
|
209
369
|
void state.onClose?.(event);
|
|
210
|
-
}
|
|
370
|
+
};
|
|
371
|
+
win.on("close", onCloseHandler);
|
|
372
|
+
registry.add(toDisposable(() => {
|
|
373
|
+
win.removeListener("close", onCloseHandler);
|
|
374
|
+
}));
|
|
375
|
+
}
|
|
376
|
+
const devToolsAccelerator = "CommandOrControl+Shift+I";
|
|
377
|
+
const registered = globalShortcut2.register(devToolsAccelerator, () => {
|
|
378
|
+
win.webContents.openDevTools({ mode: "detach" });
|
|
379
|
+
});
|
|
380
|
+
if (registered) {
|
|
381
|
+
registry.add(toDisposable(() => {
|
|
382
|
+
globalShortcut2.unregister(devToolsAccelerator);
|
|
383
|
+
}));
|
|
211
384
|
}
|
|
385
|
+
return registry;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/main/utils/sender-policy.ts
|
|
389
|
+
function createWorkbenchSenderPolicy(ctx) {
|
|
390
|
+
return (sender) => {
|
|
391
|
+
if (sender.isDestroyed()) return false;
|
|
392
|
+
if (ctx.windows.isMainSender(sender.id)) return true;
|
|
393
|
+
if (ctx.windows.isSettingsWindowSender(sender.id)) return true;
|
|
394
|
+
const settingsViewId = ctx.views.getSettingsWebContentsId();
|
|
395
|
+
if (settingsViewId != null && sender.id === settingsViewId) return true;
|
|
396
|
+
const popoverViewId = ctx.views.getPopoverWebContentsId();
|
|
397
|
+
if (popoverViewId != null && sender.id === popoverViewId) return true;
|
|
398
|
+
return false;
|
|
399
|
+
};
|
|
212
400
|
}
|
|
213
401
|
|
|
214
402
|
// src/main/services/default-adapter.ts
|
|
215
|
-
import
|
|
403
|
+
import path5 from "path";
|
|
216
404
|
import { app as app5 } from "electron";
|
|
217
405
|
var defaultAdapter = {
|
|
218
406
|
async openProject(opts) {
|
|
219
407
|
const diminaKit = await import("@dimina-kit/devkit");
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
outputDir: path3.join(app5.getPath("userData"), "dimina-fe-output"),
|
|
408
|
+
const openProjectOpts = {
|
|
409
|
+
outputDir: path5.join(app5.getPath("userData"), "dimina-fe-output"),
|
|
223
410
|
...opts,
|
|
224
411
|
simulatorDir
|
|
225
|
-
}
|
|
412
|
+
};
|
|
413
|
+
return diminaKit.openProject(openProjectOpts);
|
|
226
414
|
}
|
|
227
415
|
};
|
|
228
416
|
|
|
@@ -232,29 +420,31 @@ var SimulatorChannel = {
|
|
|
232
420
|
Detach: "simulator:detach",
|
|
233
421
|
Resize: "simulator:resize",
|
|
234
422
|
SetVisible: "simulator:setVisible",
|
|
235
|
-
Console: "simulator:console"
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
423
|
+
Console: "simulator:console"
|
|
424
|
+
};
|
|
425
|
+
var SimulatorCustomApiChannel = {
|
|
426
|
+
List: "simulator:custom-apis:list",
|
|
427
|
+
Invoke: "simulator:custom-apis:invoke"
|
|
239
428
|
};
|
|
240
429
|
var SimulatorStorageChannel = {
|
|
241
430
|
GetSnapshot: "simulator:storage:snapshot",
|
|
431
|
+
GetActivePrefix: "simulator:storage:activePrefix",
|
|
432
|
+
Set: "simulator:storage:set",
|
|
433
|
+
Remove: "simulator:storage:remove",
|
|
434
|
+
Clear: "simulator:storage:clear",
|
|
435
|
+
ClearAll: "simulator:storage:clearAll",
|
|
242
436
|
Event: "simulator:storage:event"
|
|
243
437
|
};
|
|
244
438
|
var SimulatorElementChannel = {
|
|
245
439
|
Inspect: "simulator:element:inspect",
|
|
246
440
|
Clear: "simulator:element:clear"
|
|
247
441
|
};
|
|
248
|
-
var WorkbenchChannel = {
|
|
249
|
-
GetPanelConfig: "workbench:getPanelConfig",
|
|
250
|
-
GetApiNamespaces: "workbench:getApiNamespaces",
|
|
251
|
-
Reset: "workbench:reset"
|
|
252
|
-
};
|
|
253
442
|
var WorkbenchSettingsChannel = {
|
|
254
443
|
Get: "workbenchSettings:get",
|
|
255
444
|
Save: "workbenchSettings:save",
|
|
256
445
|
SetTheme: "workbenchSettings:setTheme",
|
|
257
446
|
GetCdpStatus: "workbenchSettings:getCdpStatus",
|
|
447
|
+
GetMcpStatus: "workbenchSettings:getMcpStatus",
|
|
258
448
|
SetVisible: "workbenchSettings:setVisible",
|
|
259
449
|
Init: "workbenchSettings:init"
|
|
260
450
|
};
|
|
@@ -264,12 +454,22 @@ var ProjectChannel = {
|
|
|
264
454
|
GetPages: "project:getPages",
|
|
265
455
|
GetCompileConfig: "project:getCompileConfig",
|
|
266
456
|
SaveCompileConfig: "project:saveCompileConfig",
|
|
267
|
-
Status: "project:status"
|
|
457
|
+
Status: "project:status",
|
|
458
|
+
CaptureThumbnail: "project:captureThumbnail",
|
|
459
|
+
GetThumbnail: "project:getThumbnail"
|
|
268
460
|
};
|
|
269
461
|
var ProjectsChannel = {
|
|
270
462
|
List: "projects:list",
|
|
271
463
|
Add: "projects:add",
|
|
272
|
-
Remove: "projects:remove"
|
|
464
|
+
Remove: "projects:remove",
|
|
465
|
+
/** Phase 3: merged + sanitized template catalog for the create-project dialog. */
|
|
466
|
+
ListTemplates: "projects:listTemplates",
|
|
467
|
+
/** Phase 3: host-supplied create-project dialog hook (returns input or null). */
|
|
468
|
+
OpenCreateDialog: "projects:openCreateDialog",
|
|
469
|
+
/** Phase 3: server-side scaffold + register (delegates to create-project-service). */
|
|
470
|
+
Create: "projects:create",
|
|
471
|
+
/** Default values used to pre-fill the create-project dialog (baseDir). */
|
|
472
|
+
GetCreateDefaults: "projects:getCreateDefaults"
|
|
273
473
|
};
|
|
274
474
|
var DialogChannel = {
|
|
275
475
|
OpenDirectory: "dialog:openDirectory"
|
|
@@ -311,6 +511,13 @@ var SettingsChannel = {
|
|
|
311
511
|
Closed: "settings:closed",
|
|
312
512
|
Changed: "settings:changed"
|
|
313
513
|
};
|
|
514
|
+
var UpdateChannel = {
|
|
515
|
+
Check: "updates:check",
|
|
516
|
+
Download: "updates:download",
|
|
517
|
+
Install: "updates:install",
|
|
518
|
+
DownloadProgress: "updates:downloadProgress",
|
|
519
|
+
Available: "updates:available"
|
|
520
|
+
};
|
|
314
521
|
|
|
315
522
|
// src/main/services/notifications/renderer-notifier.ts
|
|
316
523
|
function liveWebContents(wc) {
|
|
@@ -320,8 +527,8 @@ function liveWebContents(wc) {
|
|
|
320
527
|
}
|
|
321
528
|
function createRendererNotifier(ctx) {
|
|
322
529
|
function sendToMain(channel, ...args) {
|
|
323
|
-
if (ctx.mainWindow.isDestroyed()) return;
|
|
324
|
-
const wc = liveWebContents(ctx.mainWindow.webContents);
|
|
530
|
+
if (ctx.windows.mainWindow.isDestroyed()) return;
|
|
531
|
+
const wc = liveWebContents(ctx.windows.mainWindow.webContents);
|
|
325
532
|
if (!wc) return;
|
|
326
533
|
wc.send(channel, ...args);
|
|
327
534
|
}
|
|
@@ -344,9 +551,6 @@ function createRendererNotifier(ctx) {
|
|
|
344
551
|
popoverRelaunch(config) {
|
|
345
552
|
sendToMain(PopoverChannel.Relaunch, config);
|
|
346
553
|
},
|
|
347
|
-
workbenchReset() {
|
|
348
|
-
sendToMain(WorkbenchChannel.Reset);
|
|
349
|
-
},
|
|
350
554
|
toolbarActionsChanged() {
|
|
351
555
|
sendToMain(ToolbarChannel.ActionsChanged);
|
|
352
556
|
},
|
|
@@ -371,7 +575,7 @@ function createRendererNotifier(ctx) {
|
|
|
371
575
|
|
|
372
576
|
// src/main/services/views/view-manager.ts
|
|
373
577
|
import { WebContentsView as WebContentsView2, webContents } from "electron";
|
|
374
|
-
import
|
|
578
|
+
import path6 from "path";
|
|
375
579
|
|
|
376
580
|
// src/main/services/layout/index.ts
|
|
377
581
|
var HEADER_H = 40;
|
|
@@ -420,9 +624,9 @@ function createViewManager(ctx) {
|
|
|
420
624
|
let simulatorWebContentsId = null;
|
|
421
625
|
function destroyViewInternal(view) {
|
|
422
626
|
if (!view) return;
|
|
423
|
-
if (!ctx.mainWindow.isDestroyed()) {
|
|
627
|
+
if (!ctx.windows.mainWindow.isDestroyed()) {
|
|
424
628
|
try {
|
|
425
|
-
ctx.mainWindow.contentView.removeChildView(view);
|
|
629
|
+
ctx.windows.mainWindow.contentView.removeChildView(view);
|
|
426
630
|
} catch {
|
|
427
631
|
}
|
|
428
632
|
}
|
|
@@ -434,18 +638,18 @@ function createViewManager(ctx) {
|
|
|
434
638
|
}
|
|
435
639
|
}
|
|
436
640
|
function applySimulatorBounds(simWidth) {
|
|
437
|
-
if (!simulatorView || ctx.mainWindow.isDestroyed()) return;
|
|
438
|
-
const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
|
|
641
|
+
if (!simulatorView || ctx.windows.mainWindow.isDestroyed()) return;
|
|
642
|
+
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
439
643
|
simulatorView.setBounds(computeSimulatorBounds(w, h, simWidth));
|
|
440
644
|
}
|
|
441
645
|
function applySettingsBounds() {
|
|
442
|
-
if (!settingsView || ctx.mainWindow.isDestroyed()) return;
|
|
443
|
-
const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
|
|
646
|
+
if (!settingsView || ctx.windows.mainWindow.isDestroyed()) return;
|
|
647
|
+
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
444
648
|
settingsView.setBounds(computeSettingsBounds(w, h));
|
|
445
649
|
}
|
|
446
650
|
function applyPopoverBounds() {
|
|
447
|
-
if (!popoverView || ctx.mainWindow.isDestroyed()) return;
|
|
448
|
-
const [w = 0, h = 0] = ctx.mainWindow.getContentSize();
|
|
651
|
+
if (!popoverView || ctx.windows.mainWindow.isDestroyed()) return;
|
|
652
|
+
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
449
653
|
popoverView.setBounds(computePopoverBounds(w, h));
|
|
450
654
|
}
|
|
451
655
|
function attachSimulator(simWcId, simWidth) {
|
|
@@ -493,7 +697,7 @@ function createViewManager(ctx) {
|
|
|
493
697
|
});
|
|
494
698
|
});
|
|
495
699
|
if (getDefaultTab(ctx) === "simulator") {
|
|
496
|
-
ctx.mainWindow.contentView.addChildView(simulatorView);
|
|
700
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
497
701
|
simulatorViewAdded = true;
|
|
498
702
|
applySimulatorBounds(simWidth);
|
|
499
703
|
}
|
|
@@ -512,7 +716,7 @@ function createViewManager(ctx) {
|
|
|
512
716
|
lastSimWidth = simWidth;
|
|
513
717
|
if (!simulatorView) return;
|
|
514
718
|
if (!simulatorViewAdded) {
|
|
515
|
-
ctx.mainWindow.contentView.addChildView(simulatorView);
|
|
719
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
516
720
|
simulatorViewAdded = true;
|
|
517
721
|
}
|
|
518
722
|
applySimulatorBounds(simWidth);
|
|
@@ -520,7 +724,7 @@ function createViewManager(ctx) {
|
|
|
520
724
|
function hideSimulator() {
|
|
521
725
|
if (simulatorView && simulatorViewAdded) {
|
|
522
726
|
try {
|
|
523
|
-
ctx.mainWindow.contentView.removeChildView(simulatorView);
|
|
727
|
+
ctx.windows.mainWindow.contentView.removeChildView(simulatorView);
|
|
524
728
|
} catch (e) {
|
|
525
729
|
console.error("[workbench] hideSimulator error", e);
|
|
526
730
|
}
|
|
@@ -530,14 +734,20 @@ function createViewManager(ctx) {
|
|
|
530
734
|
async function showSettings() {
|
|
531
735
|
if (!settingsView) {
|
|
532
736
|
settingsView = new WebContentsView2({
|
|
533
|
-
webPreferences: {
|
|
737
|
+
webPreferences: {
|
|
738
|
+
nodeIntegration: false,
|
|
739
|
+
contextIsolation: true,
|
|
740
|
+
sandbox: false,
|
|
741
|
+
preload: mainPreloadPath
|
|
742
|
+
}
|
|
534
743
|
});
|
|
744
|
+
applyNavigationHardening(settingsView.webContents, ctx.rendererDir);
|
|
535
745
|
await settingsView.webContents.loadFile(
|
|
536
|
-
|
|
746
|
+
path6.join(ctx.rendererDir, "entries/settings/index.html")
|
|
537
747
|
);
|
|
538
748
|
}
|
|
539
749
|
if (!settingsViewAdded) {
|
|
540
|
-
ctx.mainWindow.contentView.addChildView(settingsView);
|
|
750
|
+
ctx.windows.mainWindow.contentView.addChildView(settingsView);
|
|
541
751
|
settingsViewAdded = true;
|
|
542
752
|
}
|
|
543
753
|
applySettingsBounds();
|
|
@@ -545,7 +755,7 @@ function createViewManager(ctx) {
|
|
|
545
755
|
function hideSettings() {
|
|
546
756
|
if (settingsView && settingsViewAdded) {
|
|
547
757
|
try {
|
|
548
|
-
ctx.mainWindow.contentView.removeChildView(settingsView);
|
|
758
|
+
ctx.windows.mainWindow.contentView.removeChildView(settingsView);
|
|
549
759
|
} catch {
|
|
550
760
|
}
|
|
551
761
|
settingsViewAdded = false;
|
|
@@ -554,17 +764,23 @@ function createViewManager(ctx) {
|
|
|
554
764
|
function showPopover(data) {
|
|
555
765
|
hidePopover();
|
|
556
766
|
const popover = new WebContentsView2({
|
|
557
|
-
webPreferences: {
|
|
767
|
+
webPreferences: {
|
|
768
|
+
nodeIntegration: false,
|
|
769
|
+
contextIsolation: true,
|
|
770
|
+
sandbox: false,
|
|
771
|
+
preload: mainPreloadPath
|
|
772
|
+
}
|
|
558
773
|
});
|
|
774
|
+
applyNavigationHardening(popover.webContents, ctx.rendererDir);
|
|
559
775
|
popoverView = popover;
|
|
560
776
|
popover.setBackgroundColor("#00000000");
|
|
561
|
-
ctx.mainWindow.contentView.addChildView(popover);
|
|
777
|
+
ctx.windows.mainWindow.contentView.addChildView(popover);
|
|
562
778
|
applyPopoverBounds();
|
|
563
779
|
popover.webContents.once("did-finish-load", () => {
|
|
564
780
|
ctx.notify.popoverInit(popover, data);
|
|
565
781
|
});
|
|
566
782
|
popover.webContents.loadFile(
|
|
567
|
-
|
|
783
|
+
path6.join(ctx.rendererDir, "entries/popover/index.html")
|
|
568
784
|
);
|
|
569
785
|
}
|
|
570
786
|
function hidePopover() {
|
|
@@ -585,14 +801,6 @@ function createViewManager(ctx) {
|
|
|
585
801
|
function disposeAll() {
|
|
586
802
|
detachSimulator();
|
|
587
803
|
}
|
|
588
|
-
function logChildViews(_label) {
|
|
589
|
-
}
|
|
590
|
-
function positionSimulator(simWidth) {
|
|
591
|
-
applySimulatorBounds(simWidth);
|
|
592
|
-
}
|
|
593
|
-
function positionSettings() {
|
|
594
|
-
applySettingsBounds();
|
|
595
|
-
}
|
|
596
804
|
function resize(simWidth) {
|
|
597
805
|
lastSimWidth = simWidth;
|
|
598
806
|
if (simulatorViewAdded) applySimulatorBounds(simWidth);
|
|
@@ -618,10 +826,12 @@ function createViewManager(ctx) {
|
|
|
618
826
|
hidePopover,
|
|
619
827
|
repositionAll,
|
|
620
828
|
disposeAll,
|
|
621
|
-
logChildViews,
|
|
622
|
-
positionSimulator,
|
|
623
|
-
positionSettings,
|
|
624
829
|
getSimulatorWebContentsId: () => simulatorWebContentsId,
|
|
830
|
+
getSimulatorWebContents: () => {
|
|
831
|
+
if (simulatorWebContentsId == null) return null;
|
|
832
|
+
const wc = webContents.fromId(simulatorWebContentsId);
|
|
833
|
+
return wc && !wc.isDestroyed() ? wc : null;
|
|
834
|
+
},
|
|
625
835
|
getLastSimWidth: () => lastSimWidth,
|
|
626
836
|
isSimulatorAdded: () => simulatorViewAdded,
|
|
627
837
|
hasSimulatorView: () => simulatorView !== null,
|
|
@@ -630,19 +840,52 @@ function createViewManager(ctx) {
|
|
|
630
840
|
if (settingsView.webContents.isDestroyed()) return null;
|
|
631
841
|
return settingsView.webContents;
|
|
632
842
|
},
|
|
843
|
+
getSettingsWebContentsId: () => {
|
|
844
|
+
if (!settingsView) return null;
|
|
845
|
+
if (settingsView.webContents.isDestroyed()) return null;
|
|
846
|
+
return settingsView.webContents.id;
|
|
847
|
+
},
|
|
848
|
+
getPopoverWebContentsId: () => {
|
|
849
|
+
if (!popoverView) return null;
|
|
850
|
+
if (popoverView.webContents.isDestroyed()) return null;
|
|
851
|
+
return popoverView.webContents.id;
|
|
852
|
+
},
|
|
633
853
|
resize,
|
|
634
854
|
setVisible
|
|
635
855
|
};
|
|
636
856
|
}
|
|
637
857
|
|
|
858
|
+
// src/main/services/window-service.ts
|
|
859
|
+
function createWindowService(mainWindow) {
|
|
860
|
+
let settingsWin = null;
|
|
861
|
+
return {
|
|
862
|
+
get mainWindow() {
|
|
863
|
+
return mainWindow;
|
|
864
|
+
},
|
|
865
|
+
get settingsWindow() {
|
|
866
|
+
return settingsWin;
|
|
867
|
+
},
|
|
868
|
+
setSettingsWindow(win) {
|
|
869
|
+
settingsWin = win;
|
|
870
|
+
},
|
|
871
|
+
closeSettingsWindow() {
|
|
872
|
+
if (settingsWin && !settingsWin.isDestroyed()) settingsWin.close();
|
|
873
|
+
settingsWin = null;
|
|
874
|
+
},
|
|
875
|
+
isMainSender(id) {
|
|
876
|
+
return !mainWindow.isDestroyed() && mainWindow.webContents.id === id;
|
|
877
|
+
},
|
|
878
|
+
isSettingsWindowSender(id) {
|
|
879
|
+
return !!settingsWin && !settingsWin.isDestroyed() && settingsWin.webContents.id === id;
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
|
|
638
884
|
// src/main/services/projects/project-repository.ts
|
|
639
885
|
import { app as app6 } from "electron";
|
|
640
|
-
import
|
|
886
|
+
import path7 from "path";
|
|
641
887
|
import fs3 from "fs";
|
|
642
888
|
|
|
643
|
-
// src/shared/constants.ts
|
|
644
|
-
var DEFAULT_SCENE = 1001;
|
|
645
|
-
|
|
646
889
|
// src/main/utils/logger.ts
|
|
647
890
|
var LEVEL_PRIORITY = {
|
|
648
891
|
debug: 0,
|
|
@@ -684,7 +927,7 @@ function createLogger(tag) {
|
|
|
684
927
|
// src/main/services/projects/project-repository.ts
|
|
685
928
|
var log = createLogger("projects");
|
|
686
929
|
function getProjectsFile() {
|
|
687
|
-
return
|
|
930
|
+
return path7.join(app6.getPath("userData"), "dimina-projects.json");
|
|
688
931
|
}
|
|
689
932
|
function load() {
|
|
690
933
|
try {
|
|
@@ -706,13 +949,13 @@ function validateProjectDir(dirPath) {
|
|
|
706
949
|
if (!fs3.existsSync(dirPath)) {
|
|
707
950
|
return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
|
|
708
951
|
}
|
|
709
|
-
if (!fs3.existsSync(
|
|
710
|
-
const configPath =
|
|
952
|
+
if (!fs3.existsSync(path7.join(dirPath, "app.json"))) {
|
|
953
|
+
const configPath = path7.join(dirPath, "project.config.json");
|
|
711
954
|
if (fs3.existsSync(configPath)) {
|
|
712
955
|
try {
|
|
713
956
|
const cfg = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
714
957
|
if (cfg.miniprogramRoot) {
|
|
715
|
-
const resolvedRoot =
|
|
958
|
+
const resolvedRoot = path7.resolve(dirPath, cfg.miniprogramRoot);
|
|
716
959
|
return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
|
|
717
960
|
}
|
|
718
961
|
} catch (err) {
|
|
@@ -725,9 +968,9 @@ function validateProjectDir(dirPath) {
|
|
|
725
968
|
}
|
|
726
969
|
function addProject(dirPath) {
|
|
727
970
|
const projects = load();
|
|
728
|
-
let name =
|
|
971
|
+
let name = path7.basename(dirPath);
|
|
729
972
|
try {
|
|
730
|
-
const configPath =
|
|
973
|
+
const configPath = path7.join(dirPath, "project.config.json");
|
|
731
974
|
if (fs3.existsSync(configPath)) {
|
|
732
975
|
const cfg = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
733
976
|
if (cfg.projectname) name = cfg.projectname;
|
|
@@ -766,7 +1009,7 @@ function getCompileConfig(dirPath) {
|
|
|
766
1009
|
};
|
|
767
1010
|
}
|
|
768
1011
|
function getProjectPages(dirPath) {
|
|
769
|
-
const appJsonPath =
|
|
1012
|
+
const appJsonPath = path7.join(dirPath, "app.json");
|
|
770
1013
|
try {
|
|
771
1014
|
const appJson = JSON.parse(
|
|
772
1015
|
fs3.readFileSync(appJsonPath, "utf-8")
|
|
@@ -792,7 +1035,7 @@ function getProjectSettings(projectPath) {
|
|
|
792
1035
|
return { uploadWithSourceMap: false };
|
|
793
1036
|
}
|
|
794
1037
|
try {
|
|
795
|
-
const configPath =
|
|
1038
|
+
const configPath = path7.join(projectPath, "project.config.json");
|
|
796
1039
|
const config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
797
1040
|
return {
|
|
798
1041
|
uploadWithSourceMap: !!config.setting?.uploadWithSourceMap
|
|
@@ -803,7 +1046,7 @@ function getProjectSettings(projectPath) {
|
|
|
803
1046
|
}
|
|
804
1047
|
function updateProjectSettings(projectPath, patch) {
|
|
805
1048
|
if (!projectPath) return;
|
|
806
|
-
const configPath =
|
|
1049
|
+
const configPath = path7.join(projectPath, "project.config.json");
|
|
807
1050
|
let config = {};
|
|
808
1051
|
try {
|
|
809
1052
|
config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
@@ -819,12 +1062,19 @@ function updateProjectSettings(projectPath, patch) {
|
|
|
819
1062
|
);
|
|
820
1063
|
}
|
|
821
1064
|
|
|
1065
|
+
// src/main/services/projects/types.ts
|
|
1066
|
+
var DEFAULT_COMPILE_CONFIG = {
|
|
1067
|
+
startPage: "",
|
|
1068
|
+
scene: DEFAULT_SCENE,
|
|
1069
|
+
queryParams: []
|
|
1070
|
+
};
|
|
1071
|
+
|
|
822
1072
|
// src/main/services/workspace/workspace-service.ts
|
|
823
1073
|
function createWorkspaceService(ctx) {
|
|
824
1074
|
let currentSession = null;
|
|
825
1075
|
let currentProjectPath = "";
|
|
826
|
-
function sendStatus(
|
|
827
|
-
ctx.notify.projectStatus({ status, message });
|
|
1076
|
+
function sendStatus(status2, message, hotReload) {
|
|
1077
|
+
ctx.notify.projectStatus(hotReload ? { status: status2, message, hotReload: true } : { status: status2, message });
|
|
828
1078
|
}
|
|
829
1079
|
function bestEffort(label, fn) {
|
|
830
1080
|
try {
|
|
@@ -857,27 +1107,36 @@ function createWorkspaceService(ctx) {
|
|
|
857
1107
|
);
|
|
858
1108
|
}
|
|
859
1109
|
}
|
|
1110
|
+
const provider = ctx.projectsProvider;
|
|
860
1111
|
return {
|
|
861
|
-
listProjects: () => listProjects(),
|
|
862
|
-
addProject: (dirPath) => addProject(dirPath),
|
|
863
|
-
removeProject: (dirPath) =>
|
|
864
|
-
|
|
1112
|
+
listProjects: async () => provider.listProjects(),
|
|
1113
|
+
addProject: async (dirPath) => provider.addProject(dirPath),
|
|
1114
|
+
removeProject: async (dirPath) => {
|
|
1115
|
+
await provider.removeProject(dirPath);
|
|
1116
|
+
},
|
|
1117
|
+
hasProject: async (dirPath) => {
|
|
1118
|
+
const projects = await provider.listProjects();
|
|
1119
|
+
return projects.some((p) => p.path === dirPath);
|
|
1120
|
+
},
|
|
1121
|
+
validateProjectDir: async (dirPath) => provider.validateProjectDir ? provider.validateProjectDir(dirPath) : null,
|
|
865
1122
|
async openProject(projectPath) {
|
|
866
1123
|
clearSimulatorServicewechatReferer();
|
|
867
1124
|
await disposeSession();
|
|
868
1125
|
currentProjectPath = "";
|
|
869
|
-
const dirError = validateProjectDir(projectPath);
|
|
1126
|
+
const dirError = provider.validateProjectDir ? await provider.validateProjectDir(projectPath) : null;
|
|
870
1127
|
if (dirError) {
|
|
871
1128
|
sendStatus("error", dirError);
|
|
872
1129
|
return { success: false, error: dirError };
|
|
873
1130
|
}
|
|
874
1131
|
sendStatus("compiling", "\u6B63\u5728\u7F16\u8BD1...");
|
|
1132
|
+
const { compile } = loadWorkbenchSettings();
|
|
875
1133
|
let session2;
|
|
876
1134
|
try {
|
|
877
1135
|
session2 = await ctx.adapter.openProject({
|
|
878
1136
|
projectPath,
|
|
879
1137
|
sourcemap: true,
|
|
880
|
-
|
|
1138
|
+
watch: compile.watch,
|
|
1139
|
+
onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u70ED\u66F4\u65B0", true),
|
|
881
1140
|
onBuildError: (err) => sendStatus("error", String(err))
|
|
882
1141
|
});
|
|
883
1142
|
} catch (err) {
|
|
@@ -887,7 +1146,9 @@ function createWorkspaceService(ctx) {
|
|
|
887
1146
|
}
|
|
888
1147
|
currentSession = session2;
|
|
889
1148
|
currentProjectPath = projectPath;
|
|
890
|
-
bestEffort("updateLastOpened", () =>
|
|
1149
|
+
bestEffort("updateLastOpened", () => {
|
|
1150
|
+
if (provider.updateLastOpened) provider.updateLastOpened(projectPath);
|
|
1151
|
+
});
|
|
891
1152
|
bestEffort("sendStatus", () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210"));
|
|
892
1153
|
bestEffort("applyReferer", () => applyRefererFromSession(session2));
|
|
893
1154
|
return {
|
|
@@ -905,14 +1166,132 @@ function createWorkspaceService(ctx) {
|
|
|
905
1166
|
getSession: () => currentSession,
|
|
906
1167
|
getProjectPath: () => currentProjectPath,
|
|
907
1168
|
hasActiveSession: () => currentSession !== null,
|
|
1169
|
+
async captureThumbnail(projectPath) {
|
|
1170
|
+
const wc = ctx.views.getSimulatorWebContents();
|
|
1171
|
+
if (!wc) return null;
|
|
1172
|
+
try {
|
|
1173
|
+
const image = await wc.capturePage();
|
|
1174
|
+
const dataUrl = `data:image/png;base64,${image.toPNG().toString("base64")}`;
|
|
1175
|
+
if (provider.saveThumbnail) {
|
|
1176
|
+
await provider.saveThumbnail(projectPath, dataUrl);
|
|
1177
|
+
}
|
|
1178
|
+
return dataUrl;
|
|
1179
|
+
} catch {
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
},
|
|
1183
|
+
async getThumbnail(projectPath) {
|
|
1184
|
+
if (provider.getThumbnail) {
|
|
1185
|
+
return await provider.getThumbnail(projectPath) ?? null;
|
|
1186
|
+
}
|
|
1187
|
+
return null;
|
|
1188
|
+
},
|
|
1189
|
+
// `getProjectPages` reads the project's own `app.json` from disk and is
|
|
1190
|
+
// independent of the project registry — it stays on the repo helper.
|
|
908
1191
|
getProjectPages: (projectPath) => getProjectPages(projectPath),
|
|
909
|
-
getCompileConfig: (projectPath) => getCompileConfig(projectPath),
|
|
910
|
-
saveCompileConfig: (projectPath, config) =>
|
|
1192
|
+
getCompileConfig: async (projectPath) => provider.getCompileConfig ? await provider.getCompileConfig(projectPath) : DEFAULT_COMPILE_CONFIG,
|
|
1193
|
+
saveCompileConfig: async (projectPath, config) => {
|
|
1194
|
+
if (provider.saveCompileConfig) {
|
|
1195
|
+
await provider.saveCompileConfig(projectPath, config);
|
|
1196
|
+
}
|
|
1197
|
+
},
|
|
1198
|
+
// Per-project settings (uploadWithSourceMap etc.) live in the project's
|
|
1199
|
+
// own `project.config.json`, not the registry — keep direct repo calls.
|
|
911
1200
|
getProjectSettings: (projectPath) => getProjectSettings(projectPath),
|
|
912
1201
|
updateProjectSettings: (projectPath, patch) => updateProjectSettings(projectPath, patch)
|
|
913
1202
|
};
|
|
914
1203
|
}
|
|
915
1204
|
|
|
1205
|
+
// src/main/services/projects/thumbnail.ts
|
|
1206
|
+
import { createHash } from "crypto";
|
|
1207
|
+
import fs4 from "fs";
|
|
1208
|
+
import path8 from "path";
|
|
1209
|
+
import { app as app7 } from "electron";
|
|
1210
|
+
function getThumbnailDir() {
|
|
1211
|
+
return path8.join(app7.getPath("userData"), "thumbnails");
|
|
1212
|
+
}
|
|
1213
|
+
function hashProjectPath(projectPath) {
|
|
1214
|
+
return createHash("sha256").update(projectPath).digest("hex").slice(0, 16);
|
|
1215
|
+
}
|
|
1216
|
+
function getThumbnailPath(projectPath) {
|
|
1217
|
+
return path8.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
|
|
1218
|
+
}
|
|
1219
|
+
var DATA_URL_PNG_PREFIX = "data:image/png;base64,";
|
|
1220
|
+
function saveThumbnailFromDataUrl(projectPath, imageDataUrl) {
|
|
1221
|
+
if (!imageDataUrl.startsWith(DATA_URL_PNG_PREFIX)) return;
|
|
1222
|
+
const png = Buffer.from(imageDataUrl.slice(DATA_URL_PNG_PREFIX.length), "base64");
|
|
1223
|
+
fs4.mkdirSync(getThumbnailDir(), { recursive: true });
|
|
1224
|
+
fs4.writeFileSync(getThumbnailPath(projectPath), png);
|
|
1225
|
+
}
|
|
1226
|
+
function loadThumbnail(projectPath) {
|
|
1227
|
+
const filePath = getThumbnailPath(projectPath);
|
|
1228
|
+
try {
|
|
1229
|
+
const buf = fs4.readFileSync(filePath);
|
|
1230
|
+
return `${DATA_URL_PNG_PREFIX}${buf.toString("base64")}`;
|
|
1231
|
+
} catch {
|
|
1232
|
+
return null;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// src/main/services/projects/local-provider.ts
|
|
1237
|
+
function createLocalProjectsProvider() {
|
|
1238
|
+
return {
|
|
1239
|
+
listProjects: () => listProjects(),
|
|
1240
|
+
addProject: (dirPath) => addProject(dirPath),
|
|
1241
|
+
removeProject: (dirPath) => removeProject(dirPath),
|
|
1242
|
+
validateProjectDir: (dirPath) => validateProjectDir(dirPath),
|
|
1243
|
+
updateLastOpened: (dirPath) => updateLastOpened(dirPath),
|
|
1244
|
+
getCompileConfig: (dirPath) => getCompileConfig(dirPath),
|
|
1245
|
+
saveCompileConfig: (dirPath, cfg) => saveCompileConfig(dirPath, cfg),
|
|
1246
|
+
saveThumbnail: (dirPath, dataUrl) => saveThumbnailFromDataUrl(dirPath, dataUrl),
|
|
1247
|
+
getThumbnail: (dirPath) => loadThumbnail(dirPath)
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// src/main/services/projects/templates.ts
|
|
1252
|
+
function resolveTemplates(builtin, injected, mode) {
|
|
1253
|
+
let kept;
|
|
1254
|
+
if (mode === "none") {
|
|
1255
|
+
kept = [];
|
|
1256
|
+
} else if (mode === "all") {
|
|
1257
|
+
kept = [...builtin];
|
|
1258
|
+
} else {
|
|
1259
|
+
const byId = new Map(builtin.map((t) => [t.id, t]));
|
|
1260
|
+
kept = [];
|
|
1261
|
+
for (const id of mode) {
|
|
1262
|
+
const t = byId.get(id);
|
|
1263
|
+
if (t) kept.push(t);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
const injectedIds = new Set(injected.map((t) => t.id));
|
|
1267
|
+
const remaining = kept.filter((t) => !injectedIds.has(t.id));
|
|
1268
|
+
return [...injected, ...remaining];
|
|
1269
|
+
}
|
|
1270
|
+
function sanitizeTemplates(templates) {
|
|
1271
|
+
return templates.map((t) => {
|
|
1272
|
+
const { generate: _generate, ...rest } = t;
|
|
1273
|
+
return rest;
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
// src/main/services/projects/builtin-templates.ts
|
|
1278
|
+
import path9 from "node:path";
|
|
1279
|
+
var TEMPLATES_DIR = path9.join(devtoolsPackageRoot, "templates");
|
|
1280
|
+
var BUILTIN_TEMPLATES = [
|
|
1281
|
+
{
|
|
1282
|
+
id: "blank",
|
|
1283
|
+
name: "Blank",
|
|
1284
|
+
description: "\u6700\u5C0F\u9AA8\u67B6\uFF1Aapp.* + \u4E00\u4E2A index \u9875\u9762",
|
|
1285
|
+
source: { type: "directory", path: path9.join(TEMPLATES_DIR, "blank") }
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
id: "taro-todo",
|
|
1289
|
+
name: "Taro Todo",
|
|
1290
|
+
description: "Taro \u7F16\u8BD1\u4EA7\u7269\u7684 Todo \u793A\u4F8B",
|
|
1291
|
+
source: { type: "directory", path: path9.join(TEMPLATES_DIR, "taro-todo") }
|
|
1292
|
+
}
|
|
1293
|
+
];
|
|
1294
|
+
|
|
916
1295
|
// src/main/services/workbench-context.ts
|
|
917
1296
|
function hasBuiltinPanel(ctx, panelId) {
|
|
918
1297
|
return ctx.panels.includes(panelId);
|
|
@@ -924,7 +1303,6 @@ function getDefaultTab(ctx) {
|
|
|
924
1303
|
}
|
|
925
1304
|
function createWorkbenchContext(opts) {
|
|
926
1305
|
const ctx = {
|
|
927
|
-
mainWindow: opts.mainWindow,
|
|
928
1306
|
adapter: opts.adapter ?? defaultAdapter,
|
|
929
1307
|
preloadPath: opts.preloadPath,
|
|
930
1308
|
rendererDir: opts.rendererDir,
|
|
@@ -932,12 +1310,21 @@ function createWorkbenchContext(opts) {
|
|
|
932
1310
|
apiNamespaces: opts.apiNamespaces ?? [],
|
|
933
1311
|
appName: opts.appName ?? "Dimina DevTools",
|
|
934
1312
|
toolbarActions: opts.toolbarActions,
|
|
935
|
-
brandingProvider: opts.brandingProvider
|
|
936
|
-
workbenchSettingsWindow: null
|
|
1313
|
+
brandingProvider: opts.brandingProvider
|
|
937
1314
|
};
|
|
1315
|
+
ctx.registry = new DisposableRegistry();
|
|
1316
|
+
ctx.windows = createWindowService(opts.mainWindow);
|
|
938
1317
|
ctx.views = createViewManager(ctx);
|
|
939
1318
|
ctx.notify = createRendererNotifier(ctx);
|
|
1319
|
+
ctx.projectsProvider = opts.projectsProvider ?? createLocalProjectsProvider();
|
|
1320
|
+
ctx.projectTemplates = resolveTemplates(
|
|
1321
|
+
BUILTIN_TEMPLATES,
|
|
1322
|
+
opts.projectTemplates ?? [],
|
|
1323
|
+
opts.builtinTemplates ?? "all"
|
|
1324
|
+
);
|
|
1325
|
+
ctx.customCreateProjectDialog = opts.customCreateProjectDialog;
|
|
940
1326
|
ctx.workspace = createWorkspaceService(ctx);
|
|
1327
|
+
ctx.senderPolicy = createWorkbenchSenderPolicy(ctx);
|
|
941
1328
|
return ctx;
|
|
942
1329
|
}
|
|
943
1330
|
|
|
@@ -967,6 +1354,17 @@ function installAppMenu(ctx) {
|
|
|
967
1354
|
{ role: "quit" }
|
|
968
1355
|
]
|
|
969
1356
|
},
|
|
1357
|
+
{
|
|
1358
|
+
label: "\u9879\u76EE",
|
|
1359
|
+
submenu: [
|
|
1360
|
+
{
|
|
1361
|
+
label: "\u6253\u5F00\u9879\u76EE",
|
|
1362
|
+
click: () => {
|
|
1363
|
+
ctx.notify.windowNavigateBack();
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
]
|
|
1367
|
+
},
|
|
970
1368
|
{
|
|
971
1369
|
label: "\u7F16\u8F91",
|
|
972
1370
|
submenu: [
|
|
@@ -1008,72 +1406,256 @@ function installAppMenu(ctx) {
|
|
|
1008
1406
|
}
|
|
1009
1407
|
|
|
1010
1408
|
// src/main/ipc/app.ts
|
|
1409
|
+
import fs5 from "fs";
|
|
1410
|
+
|
|
1411
|
+
// src/main/utils/ipc-registry.ts
|
|
1011
1412
|
import { ipcMain } from "electron";
|
|
1012
|
-
|
|
1413
|
+
|
|
1414
|
+
// src/main/utils/ipc-schema.ts
|
|
1415
|
+
var IpcValidationError = class extends Error {
|
|
1416
|
+
channel;
|
|
1417
|
+
paths;
|
|
1418
|
+
constructor(channel, issues) {
|
|
1419
|
+
const paths = issues.map((i) => i.path.map((p) => String(p)).join(".") || "<root>");
|
|
1420
|
+
super(`Invalid IPC arguments for ${channel}`);
|
|
1421
|
+
this.name = "IpcValidationError";
|
|
1422
|
+
this.channel = channel;
|
|
1423
|
+
this.paths = paths;
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
function validate(channel, schema, args) {
|
|
1427
|
+
const r = schema.safeParse(args);
|
|
1428
|
+
if (!r.success) {
|
|
1429
|
+
console.warn("[ipc] schema reject", channel, r.error.issues);
|
|
1430
|
+
throw new IpcValidationError(channel, r.error.issues);
|
|
1431
|
+
}
|
|
1432
|
+
return r.data;
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
// src/main/utils/ipc-registry.ts
|
|
1436
|
+
var log2 = createLogger("ipc");
|
|
1437
|
+
function reportListenerError(channel, err) {
|
|
1438
|
+
if (err instanceof IpcValidationError) {
|
|
1439
|
+
log2.warn(`schema reject on '${channel}' at [${err.paths.join(", ")}]`);
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
if (err instanceof Error) {
|
|
1443
|
+
log2.error(`listener threw on '${channel}': ${err.message}`, err.stack);
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
log2.error(`listener threw on '${channel}'`, err);
|
|
1447
|
+
}
|
|
1448
|
+
function isThenable(v) {
|
|
1449
|
+
return v != null && (typeof v === "object" || typeof v === "function") && typeof v.then === "function";
|
|
1450
|
+
}
|
|
1451
|
+
function summarizeSender(sender) {
|
|
1452
|
+
if (sender.isDestroyed()) return "<destroyed>";
|
|
1453
|
+
const url = sender.getURL();
|
|
1454
|
+
return `id=${sender.id} ${url.slice(0, 120)}`;
|
|
1455
|
+
}
|
|
1456
|
+
var IpcRegistry = class {
|
|
1457
|
+
constructor(policy) {
|
|
1458
|
+
this.policy = policy;
|
|
1459
|
+
}
|
|
1460
|
+
policy;
|
|
1461
|
+
registry = new DisposableRegistry();
|
|
1462
|
+
handle(channel, fn) {
|
|
1463
|
+
const policy = this.policy;
|
|
1464
|
+
const guarded = policy ? (event, ...args) => {
|
|
1465
|
+
const sender = event.sender;
|
|
1466
|
+
if (!policy(sender)) {
|
|
1467
|
+
console.warn(
|
|
1468
|
+
`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`
|
|
1469
|
+
);
|
|
1470
|
+
throw new Error(`IPC sender rejected for channel ${channel}`);
|
|
1471
|
+
}
|
|
1472
|
+
return fn(event, ...args);
|
|
1473
|
+
} : fn;
|
|
1474
|
+
ipcMain.handle(channel, guarded);
|
|
1475
|
+
this.registry.add(() => ipcMain.removeHandler(channel));
|
|
1476
|
+
return this;
|
|
1477
|
+
}
|
|
1478
|
+
on(channel, fn) {
|
|
1479
|
+
const policy = this.policy;
|
|
1480
|
+
const raw = fn;
|
|
1481
|
+
const safeInvoke = (event, args) => {
|
|
1482
|
+
try {
|
|
1483
|
+
const ret = raw(event, ...args);
|
|
1484
|
+
if (isThenable(ret)) {
|
|
1485
|
+
Promise.resolve(ret).catch((err) => reportListenerError(channel, err));
|
|
1486
|
+
}
|
|
1487
|
+
} catch (err) {
|
|
1488
|
+
reportListenerError(channel, err);
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
const guarded = policy ? (event, ...args) => {
|
|
1492
|
+
const sender = event.sender;
|
|
1493
|
+
if (!policy(sender)) {
|
|
1494
|
+
console.warn(
|
|
1495
|
+
`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`
|
|
1496
|
+
);
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
safeInvoke(event, args);
|
|
1500
|
+
} : (event, ...args) => safeInvoke(event, args);
|
|
1501
|
+
ipcMain.on(channel, guarded);
|
|
1502
|
+
this.registry.add(() => {
|
|
1503
|
+
ipcMain.removeListener(channel, guarded);
|
|
1504
|
+
});
|
|
1505
|
+
return this;
|
|
1506
|
+
}
|
|
1507
|
+
dispose() {
|
|
1508
|
+
return this.registry.dispose();
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
|
|
1512
|
+
// src/main/ipc/app.ts
|
|
1013
1513
|
function registerAppIpc(ctx) {
|
|
1014
|
-
|
|
1015
|
-
return `file://${
|
|
1016
|
-
})
|
|
1017
|
-
ipcMain.handle(AppChannel.GetBranding, async () => {
|
|
1514
|
+
return new IpcRegistry(ctx.senderPolicy).handle(AppChannel.GetPreloadPath, () => {
|
|
1515
|
+
return `file://${fs5.realpathSync(ctx.preloadPath)}`;
|
|
1516
|
+
}).handle(AppChannel.GetBranding, async () => {
|
|
1018
1517
|
if (ctx.brandingProvider) return ctx.brandingProvider();
|
|
1019
1518
|
return { appName: ctx.appName };
|
|
1020
1519
|
});
|
|
1021
1520
|
}
|
|
1022
1521
|
|
|
1522
|
+
// src/shared/ipc-schemas.ts
|
|
1523
|
+
import { z } from "zod";
|
|
1524
|
+
var AbsolutePath = z.string().min(1).refine(
|
|
1525
|
+
(p) => p.startsWith("/") || /^[a-zA-Z]:\\/.test(p) || /^[a-zA-Z]:\//.test(p),
|
|
1526
|
+
"must be an absolute path"
|
|
1527
|
+
);
|
|
1528
|
+
var PanelEvalSchema = z.tuple([z.string().min(1).max(5e4)]);
|
|
1529
|
+
var ProjectsAddSchema = z.tuple([AbsolutePath]);
|
|
1530
|
+
var ProjectOpenSchema = z.tuple([AbsolutePath]);
|
|
1531
|
+
var CompileConfigShape = z.looseObject({
|
|
1532
|
+
startPage: z.string().optional(),
|
|
1533
|
+
scene: z.number().optional(),
|
|
1534
|
+
queryParams: z.array(z.looseObject({ key: z.string(), value: z.string() })).optional()
|
|
1535
|
+
});
|
|
1536
|
+
var ProjectSaveCompileConfigSchema = z.tuple([
|
|
1537
|
+
AbsolutePath,
|
|
1538
|
+
CompileConfigShape
|
|
1539
|
+
]);
|
|
1540
|
+
var PopoverShowSchema = z.tuple([z.looseObject({})]);
|
|
1541
|
+
var SimWidth = z.number().int().min(100).max(2e3);
|
|
1542
|
+
var SimulatorAttachSchema = z.tuple([
|
|
1543
|
+
z.number().int().positive(),
|
|
1544
|
+
SimWidth
|
|
1545
|
+
]);
|
|
1546
|
+
var SimulatorResizeSchema = z.tuple([SimWidth]);
|
|
1547
|
+
var SimulatorSetVisibleSchema = z.tuple([z.boolean(), SimWidth]);
|
|
1548
|
+
var SimulatorCustomApiInvokeSchema = z.tuple([
|
|
1549
|
+
z.string().min(1).max(256),
|
|
1550
|
+
z.unknown()
|
|
1551
|
+
]);
|
|
1552
|
+
var ProjectGetPagesSchema = z.tuple([AbsolutePath]);
|
|
1553
|
+
var ProjectGetCompileConfigSchema = z.tuple([AbsolutePath]);
|
|
1554
|
+
var ProjectsRemoveSchema = z.tuple([AbsolutePath]);
|
|
1555
|
+
var WorkbenchSettingsSaveSchema = z.tuple([
|
|
1556
|
+
z.looseObject({
|
|
1557
|
+
cdp: z.looseObject({
|
|
1558
|
+
enabled: z.boolean(),
|
|
1559
|
+
port: z.number().int().min(0).max(65535)
|
|
1560
|
+
}),
|
|
1561
|
+
mcp: z.looseObject({
|
|
1562
|
+
enabled: z.boolean(),
|
|
1563
|
+
port: z.number().int().min(0).max(65535)
|
|
1564
|
+
}),
|
|
1565
|
+
compile: z.looseObject({
|
|
1566
|
+
watch: z.boolean()
|
|
1567
|
+
}),
|
|
1568
|
+
theme: z.enum(["system", "light", "dark"]),
|
|
1569
|
+
lastCreateBaseDir: z.union([z.string(), z.null()])
|
|
1570
|
+
})
|
|
1571
|
+
]);
|
|
1572
|
+
var WorkbenchSettingsSetThemeSchema = z.tuple([
|
|
1573
|
+
z.enum(["system", "light", "dark"])
|
|
1574
|
+
]);
|
|
1575
|
+
var WorkbenchSettingsSetVisibleSchema = z.tuple([z.boolean()]);
|
|
1576
|
+
var SettingsSetVisibleSchema = z.tuple([z.boolean()]);
|
|
1577
|
+
var SettingsConfigChangedSchema = z.tuple([CompileConfigShape]);
|
|
1578
|
+
var PopoverRelaunchSchema = z.tuple([CompileConfigShape]);
|
|
1579
|
+
var SettingsProjectSettingsChangedSchema = z.tuple([
|
|
1580
|
+
z.looseObject({
|
|
1581
|
+
uploadWithSourceMap: z.boolean().optional()
|
|
1582
|
+
})
|
|
1583
|
+
]);
|
|
1584
|
+
var PanelSelectSchema = z.tuple([z.string().min(1).max(200)]);
|
|
1585
|
+
var ProjectCaptureThumbnailSchema = z.tuple([AbsolutePath]);
|
|
1586
|
+
var ProjectGetThumbnailSchema = z.tuple([AbsolutePath]);
|
|
1587
|
+
|
|
1588
|
+
// src/main/services/simulator/custom-apis.ts
|
|
1589
|
+
function createSimulatorApiRegistry() {
|
|
1590
|
+
const handlers2 = /* @__PURE__ */ new Map();
|
|
1591
|
+
return {
|
|
1592
|
+
register(name, handler) {
|
|
1593
|
+
handlers2.set(name, handler);
|
|
1594
|
+
return () => {
|
|
1595
|
+
if (handlers2.get(name) === handler) handlers2.delete(name);
|
|
1596
|
+
};
|
|
1597
|
+
},
|
|
1598
|
+
list() {
|
|
1599
|
+
return Array.from(handlers2.keys());
|
|
1600
|
+
},
|
|
1601
|
+
async invoke(name, params) {
|
|
1602
|
+
const handler = handlers2.get(name);
|
|
1603
|
+
if (!handler) throw new Error(`Simulator API "${name}" is not registered`);
|
|
1604
|
+
return await handler(params);
|
|
1605
|
+
},
|
|
1606
|
+
clear() {
|
|
1607
|
+
handlers2.clear();
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
var simulatorApiRegistry = createSimulatorApiRegistry();
|
|
1612
|
+
|
|
1023
1613
|
// src/main/ipc/simulator.ts
|
|
1024
|
-
import { ipcMain as ipcMain2 } from "electron";
|
|
1025
1614
|
function registerSimulatorIpc(ctx) {
|
|
1026
|
-
|
|
1615
|
+
return new IpcRegistry(ctx.senderPolicy).handle(SimulatorChannel.Attach, (_, ...args) => {
|
|
1616
|
+
const [simWcId, simWidth] = validate(SimulatorChannel.Attach, SimulatorAttachSchema, args);
|
|
1027
1617
|
ctx.views.attachSimulator(simWcId, simWidth);
|
|
1028
|
-
})
|
|
1029
|
-
ipcMain2.handle(SimulatorChannel.Detach, () => {
|
|
1618
|
+
}).handle(SimulatorChannel.Detach, () => {
|
|
1030
1619
|
ctx.views.detachSimulator();
|
|
1031
|
-
})
|
|
1032
|
-
|
|
1620
|
+
}).handle(SimulatorChannel.Resize, (_, ...args) => {
|
|
1621
|
+
const [simWidth] = validate(SimulatorChannel.Resize, SimulatorResizeSchema, args);
|
|
1033
1622
|
ctx.views.resize(simWidth);
|
|
1034
|
-
})
|
|
1035
|
-
|
|
1623
|
+
}).handle(SimulatorChannel.SetVisible, (_, ...args) => {
|
|
1624
|
+
const [visible, simWidth] = validate(SimulatorChannel.SetVisible, SimulatorSetVisibleSchema, args);
|
|
1036
1625
|
ctx.views.setVisible(visible, simWidth);
|
|
1037
|
-
})
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
return ctx.apiNamespaces;
|
|
1043
|
-
});
|
|
1044
|
-
ipcMain2.on(WorkbenchChannel.Reset, () => {
|
|
1045
|
-
ctx.notify.workbenchReset();
|
|
1626
|
+
}).handle(SimulatorCustomApiChannel.List, () => {
|
|
1627
|
+
return simulatorApiRegistry.list();
|
|
1628
|
+
}).handle(SimulatorCustomApiChannel.Invoke, (_, ...args) => {
|
|
1629
|
+
const [name, params] = validate(SimulatorCustomApiChannel.Invoke, SimulatorCustomApiInvokeSchema, args);
|
|
1630
|
+
return simulatorApiRegistry.invoke(name, params);
|
|
1046
1631
|
});
|
|
1047
1632
|
}
|
|
1048
1633
|
|
|
1049
1634
|
// src/main/ipc/panels.ts
|
|
1050
|
-
import { ipcMain as ipcMain3, webContents as webContents2 } from "electron";
|
|
1051
1635
|
var BUILTIN_PANELS = [
|
|
1052
1636
|
{ id: "wxml", label: "WXML" },
|
|
1053
1637
|
{ id: "appdata", label: "AppData" },
|
|
1054
1638
|
{ id: "storage", label: "Storage" }
|
|
1055
1639
|
];
|
|
1056
1640
|
function registerPanelsIpc(ctx) {
|
|
1057
|
-
|
|
1641
|
+
return new IpcRegistry(ctx.senderPolicy).handle(PanelChannel.List, () => {
|
|
1058
1642
|
return BUILTIN_PANELS.filter((p) => ctx.panels.includes(p.id));
|
|
1059
|
-
})
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
if (!sim || sim.isDestroyed()) return void 0;
|
|
1643
|
+
}).handle(PanelChannel.Eval, async (_event, ...args) => {
|
|
1644
|
+
const [expression] = validate(PanelChannel.Eval, PanelEvalSchema, args);
|
|
1645
|
+
if (!ctx.workspace.hasActiveSession()) return void 0;
|
|
1646
|
+
const sim = ctx.views.getSimulatorWebContents();
|
|
1647
|
+
if (!sim) return void 0;
|
|
1065
1648
|
try {
|
|
1066
1649
|
return await sim.executeJavaScript(expression);
|
|
1067
1650
|
} catch {
|
|
1068
1651
|
return void 0;
|
|
1069
1652
|
}
|
|
1070
|
-
})
|
|
1071
|
-
|
|
1653
|
+
}).handle(PanelChannel.Select, (_event, ...args) => {
|
|
1654
|
+
validate(PanelChannel.Select, PanelSelectSchema, args);
|
|
1072
1655
|
if (ctx.views.hasSimulatorView() && ctx.views.isSimulatorAdded()) {
|
|
1073
1656
|
ctx.views.hideSimulator();
|
|
1074
1657
|
}
|
|
1075
|
-
})
|
|
1076
|
-
ipcMain3.handle(PanelChannel.SelectSimulator, () => {
|
|
1658
|
+
}).handle(PanelChannel.SelectSimulator, () => {
|
|
1077
1659
|
if (ctx.views.hasSimulatorView() && !ctx.views.isSimulatorAdded()) {
|
|
1078
1660
|
ctx.views.showSimulator(ctx.views.getLastSimWidth());
|
|
1079
1661
|
}
|
|
@@ -1081,79 +1663,272 @@ function registerPanelsIpc(ctx) {
|
|
|
1081
1663
|
}
|
|
1082
1664
|
|
|
1083
1665
|
// src/main/ipc/popover.ts
|
|
1084
|
-
import { ipcMain as ipcMain4 } from "electron";
|
|
1085
1666
|
function registerPopoverIpc(ctx) {
|
|
1086
|
-
|
|
1667
|
+
return new IpcRegistry(ctx.senderPolicy).handle(PopoverChannel.Show, (_event, ...args) => {
|
|
1668
|
+
const [data] = validate(PopoverChannel.Show, PopoverShowSchema, args);
|
|
1087
1669
|
ctx.views.showPopover(data);
|
|
1088
|
-
})
|
|
1089
|
-
|
|
1090
|
-
|
|
1670
|
+
}).handle(PopoverChannel.Hide, () => {
|
|
1671
|
+
ctx.views.hidePopover();
|
|
1672
|
+
}).on(PopoverChannel.Relaunch, (_event, ...args) => {
|
|
1673
|
+
const [newConfig] = validate(PopoverChannel.Relaunch, PopoverRelaunchSchema, args);
|
|
1091
1674
|
ctx.views.hidePopover();
|
|
1092
1675
|
ctx.notify.popoverRelaunch(newConfig);
|
|
1093
1676
|
});
|
|
1094
1677
|
}
|
|
1678
|
+
var popoverModule = {
|
|
1679
|
+
setup: (ctx) => registerPopoverIpc(ctx)
|
|
1680
|
+
};
|
|
1681
|
+
|
|
1682
|
+
// src/main/ipc/projects.ts
|
|
1683
|
+
import { app as app8, dialog } from "electron";
|
|
1684
|
+
import path11 from "path";
|
|
1685
|
+
|
|
1686
|
+
// src/main/services/projects/create-project-service.ts
|
|
1687
|
+
import fs6 from "node:fs";
|
|
1688
|
+
import path10 from "node:path";
|
|
1689
|
+
var DEFAULT_TEMPLATE_ID = "blank";
|
|
1690
|
+
async function createProject(input, ctx) {
|
|
1691
|
+
const name = (input.name ?? "").trim();
|
|
1692
|
+
if (name.length === 0) {
|
|
1693
|
+
throw new Error("Project name cannot be empty");
|
|
1694
|
+
}
|
|
1695
|
+
const target = input.path;
|
|
1696
|
+
if (!target || typeof target !== "string") {
|
|
1697
|
+
throw new Error("Project path is required");
|
|
1698
|
+
}
|
|
1699
|
+
if (fs6.existsSync(target)) {
|
|
1700
|
+
const stat = fs6.statSync(target);
|
|
1701
|
+
if (!stat.isDirectory()) {
|
|
1702
|
+
throw new Error(`Project path exists and is not a directory: ${target}`);
|
|
1703
|
+
}
|
|
1704
|
+
const entries = fs6.readdirSync(target);
|
|
1705
|
+
if (entries.length > 0) {
|
|
1706
|
+
throw new Error(
|
|
1707
|
+
`Project path is not empty (refusing to overwrite): ${target}`
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
const templateId = input.templateId ?? DEFAULT_TEMPLATE_ID;
|
|
1712
|
+
const template = ctx.templates.find((t) => t.id === templateId);
|
|
1713
|
+
if (!template) {
|
|
1714
|
+
throw new Error(`Template not found: ${templateId}`);
|
|
1715
|
+
}
|
|
1716
|
+
if (!template.source && !template.generate) {
|
|
1717
|
+
throw new Error(
|
|
1718
|
+
`Template '${templateId}' has neither a source directory nor a generate function`
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
fs6.mkdirSync(target, { recursive: true });
|
|
1722
|
+
if (template.generate) {
|
|
1723
|
+
await template.generate(target, { name });
|
|
1724
|
+
} else if (template.source) {
|
|
1725
|
+
if (!fs6.existsSync(template.source.path)) {
|
|
1726
|
+
throw new Error(
|
|
1727
|
+
`Template source missing on disk: ${template.source.path}`
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
fs6.cpSync(template.source.path, target, {
|
|
1731
|
+
recursive: true,
|
|
1732
|
+
force: true
|
|
1733
|
+
});
|
|
1734
|
+
}
|
|
1735
|
+
const cfgPath = path10.join(target, "project.config.json");
|
|
1736
|
+
let cfg = {};
|
|
1737
|
+
if (fs6.existsSync(cfgPath)) {
|
|
1738
|
+
try {
|
|
1739
|
+
cfg = JSON.parse(fs6.readFileSync(cfgPath, "utf-8"));
|
|
1740
|
+
} catch {
|
|
1741
|
+
cfg = {};
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
cfg.projectname = name;
|
|
1745
|
+
fs6.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
1746
|
+
const created = await ctx.projectsProvider.addProject(target);
|
|
1747
|
+
return created;
|
|
1748
|
+
}
|
|
1095
1749
|
|
|
1096
1750
|
// src/main/ipc/projects.ts
|
|
1097
|
-
import { ipcMain as ipcMain5, dialog } from "electron";
|
|
1098
1751
|
function registerProjectsIpc(ctx) {
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1752
|
+
return new IpcRegistry(ctx.senderPolicy).handle(ProjectsChannel.List, () => {
|
|
1753
|
+
return ctx.workspace.listProjects();
|
|
1754
|
+
}).handle(DialogChannel.OpenDirectory, async () => {
|
|
1755
|
+
const result = await dialog.showOpenDialog(ctx.windows.mainWindow, {
|
|
1102
1756
|
properties: ["openDirectory"],
|
|
1103
1757
|
title: "\u9009\u62E9\u5C0F\u7A0B\u5E8F\u9879\u76EE\u76EE\u5F55"
|
|
1104
1758
|
});
|
|
1105
1759
|
return result.canceled ? null : result.filePaths[0];
|
|
1760
|
+
}).handle(ProjectsChannel.Add, async (_event, ...args) => {
|
|
1761
|
+
const [dirPath] = validate(ProjectsChannel.Add, ProjectsAddSchema, args);
|
|
1762
|
+
const dirError = await ctx.workspace.validateProjectDir(dirPath);
|
|
1763
|
+
if (dirError) {
|
|
1764
|
+
await dialog.showMessageBox(ctx.windows.mainWindow, {
|
|
1765
|
+
type: "error",
|
|
1766
|
+
title: "\u65E0\u6CD5\u5BFC\u5165\u9879\u76EE",
|
|
1767
|
+
message: "\u8BE5\u76EE\u5F55\u4E0D\u662F\u6709\u6548\u7684\u5C0F\u7A0B\u5E8F\u9879\u76EE",
|
|
1768
|
+
detail: dirError,
|
|
1769
|
+
buttons: ["\u786E\u5B9A"]
|
|
1770
|
+
});
|
|
1771
|
+
throw new Error(dirError);
|
|
1772
|
+
}
|
|
1773
|
+
const duplicate = await ctx.workspace.hasProject(dirPath);
|
|
1774
|
+
const project = await ctx.workspace.addProject(dirPath);
|
|
1775
|
+
if (duplicate) {
|
|
1776
|
+
await dialog.showMessageBox(ctx.windows.mainWindow, {
|
|
1777
|
+
type: "info",
|
|
1778
|
+
title: "\u9879\u76EE\u5DF2\u5B58\u5728",
|
|
1779
|
+
message: "\u8BE5\u9879\u76EE\u5DF2\u5728\u5217\u8868\u4E2D",
|
|
1780
|
+
detail: dirPath,
|
|
1781
|
+
buttons: ["\u786E\u5B9A"]
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
return project;
|
|
1785
|
+
}).handle(ProjectsChannel.Remove, (_event, ...args) => {
|
|
1786
|
+
const [dirPath] = validate(ProjectsChannel.Remove, ProjectsRemoveSchema, args);
|
|
1787
|
+
return ctx.workspace.removeProject(dirPath);
|
|
1788
|
+
}).handle(ProjectsChannel.ListTemplates, () => {
|
|
1789
|
+
return sanitizeTemplates(ctx.projectTemplates ?? []);
|
|
1790
|
+
}).handle(ProjectsChannel.OpenCreateDialog, async () => {
|
|
1791
|
+
if (!ctx.customCreateProjectDialog) return null;
|
|
1792
|
+
const sanitized = sanitizeTemplates(ctx.projectTemplates ?? []);
|
|
1793
|
+
return await ctx.customCreateProjectDialog({
|
|
1794
|
+
parentWindow: ctx.windows.mainWindow,
|
|
1795
|
+
templates: sanitized
|
|
1796
|
+
});
|
|
1797
|
+
}).handle(ProjectsChannel.Create, async (_event, ...args) => {
|
|
1798
|
+
const [raw] = args;
|
|
1799
|
+
if (!raw || typeof raw !== "object") {
|
|
1800
|
+
throw new Error("projects:create expects a CreateProjectInput object");
|
|
1801
|
+
}
|
|
1802
|
+
const input = raw;
|
|
1803
|
+
let project;
|
|
1804
|
+
try {
|
|
1805
|
+
project = await createProject(input, {
|
|
1806
|
+
templates: ctx.projectTemplates ?? [],
|
|
1807
|
+
projectsProvider: ctx.projectsProvider
|
|
1808
|
+
});
|
|
1809
|
+
} catch (err) {
|
|
1810
|
+
await dialog.showMessageBox(ctx.windows.mainWindow, {
|
|
1811
|
+
type: "error",
|
|
1812
|
+
title: "\u521B\u5EFA\u9879\u76EE\u5931\u8D25",
|
|
1813
|
+
message: "\u65E0\u6CD5\u521B\u5EFA\u9879\u76EE",
|
|
1814
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
1815
|
+
buttons: ["\u786E\u5B9A"]
|
|
1816
|
+
});
|
|
1817
|
+
throw err;
|
|
1818
|
+
}
|
|
1819
|
+
try {
|
|
1820
|
+
const settings = loadWorkbenchSettings();
|
|
1821
|
+
const newBase = path11.dirname(input.path);
|
|
1822
|
+
if (newBase && settings.lastCreateBaseDir !== newBase) {
|
|
1823
|
+
saveWorkbenchSettings({ ...settings, lastCreateBaseDir: newBase });
|
|
1824
|
+
}
|
|
1825
|
+
} catch (err) {
|
|
1826
|
+
console.warn("[projects:create] failed to persist lastCreateBaseDir", err);
|
|
1827
|
+
}
|
|
1828
|
+
return project;
|
|
1829
|
+
}).handle(ProjectsChannel.GetCreateDefaults, () => {
|
|
1830
|
+
const settings = loadWorkbenchSettings();
|
|
1831
|
+
const baseDir = settings.lastCreateBaseDir ?? safeAppPath("documents") ?? safeAppPath("home") ?? "";
|
|
1832
|
+
return { baseDir };
|
|
1106
1833
|
});
|
|
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
1834
|
}
|
|
1835
|
+
function safeAppPath(name) {
|
|
1836
|
+
try {
|
|
1837
|
+
return app8.getPath(name);
|
|
1838
|
+
} catch {
|
|
1839
|
+
return null;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
var projectsModule = {
|
|
1843
|
+
setup: (ctx) => registerProjectsIpc(ctx)
|
|
1844
|
+
};
|
|
1116
1845
|
|
|
1117
1846
|
// src/main/ipc/session.ts
|
|
1118
|
-
import { ipcMain as ipcMain6 } from "electron";
|
|
1119
1847
|
function registerSessionIpc(ctx) {
|
|
1120
|
-
|
|
1121
|
-
ProjectChannel.Open,
|
|
1122
|
-
|
|
1123
|
-
)
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1848
|
+
return new IpcRegistry(ctx.senderPolicy).handle(ProjectChannel.Open, (_, ...args) => {
|
|
1849
|
+
const [projectPath] = validate(ProjectChannel.Open, ProjectOpenSchema, args);
|
|
1850
|
+
return ctx.workspace.openProject(projectPath);
|
|
1851
|
+
}).handle(ProjectChannel.GetPages, (_, ...args) => {
|
|
1852
|
+
const [projectPath] = validate(ProjectChannel.GetPages, ProjectGetPagesSchema, args);
|
|
1853
|
+
return ctx.workspace.getProjectPages(projectPath);
|
|
1854
|
+
}).handle(ProjectChannel.GetCompileConfig, (_, ...args) => {
|
|
1855
|
+
const [projectPath] = validate(
|
|
1856
|
+
ProjectChannel.GetCompileConfig,
|
|
1857
|
+
ProjectGetCompileConfigSchema,
|
|
1858
|
+
args
|
|
1859
|
+
);
|
|
1860
|
+
return ctx.workspace.getCompileConfig(projectPath);
|
|
1861
|
+
}).handle(ProjectChannel.SaveCompileConfig, (_, ...args) => {
|
|
1862
|
+
const [projectPath, config] = validate(
|
|
1863
|
+
ProjectChannel.SaveCompileConfig,
|
|
1864
|
+
ProjectSaveCompileConfigSchema,
|
|
1865
|
+
args
|
|
1866
|
+
);
|
|
1867
|
+
return ctx.workspace.saveCompileConfig(projectPath, config);
|
|
1868
|
+
}).handle(ProjectChannel.Close, () => {
|
|
1869
|
+
return ctx.workspace.closeProject();
|
|
1870
|
+
}).handle(ProjectChannel.CaptureThumbnail, (_, ...args) => {
|
|
1871
|
+
const [projectPath] = validate(
|
|
1872
|
+
ProjectChannel.CaptureThumbnail,
|
|
1873
|
+
ProjectCaptureThumbnailSchema,
|
|
1874
|
+
args
|
|
1875
|
+
);
|
|
1876
|
+
return ctx.workspace.captureThumbnail(projectPath);
|
|
1877
|
+
}).handle(ProjectChannel.GetThumbnail, (_, ...args) => {
|
|
1878
|
+
const [projectPath] = validate(
|
|
1879
|
+
ProjectChannel.GetThumbnail,
|
|
1880
|
+
ProjectGetThumbnailSchema,
|
|
1881
|
+
args
|
|
1882
|
+
);
|
|
1883
|
+
return ctx.workspace.getThumbnail(projectPath);
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
var sessionModule = {
|
|
1887
|
+
setup: (ctx) => registerSessionIpc(ctx)
|
|
1888
|
+
};
|
|
1889
|
+
|
|
1890
|
+
// src/main/ipc/settings.ts
|
|
1891
|
+
import { app as app9 } from "electron";
|
|
1892
|
+
|
|
1893
|
+
// src/main/services/mcp/status.ts
|
|
1894
|
+
var status = { running: false, port: null, error: null };
|
|
1895
|
+
function getMcpStatus() {
|
|
1896
|
+
return { ...status };
|
|
1897
|
+
}
|
|
1898
|
+
function recordMcpStarted(port) {
|
|
1899
|
+
status = { running: true, port, error: null };
|
|
1900
|
+
}
|
|
1901
|
+
function recordMcpFailed(error) {
|
|
1902
|
+
status = { running: false, port: null, error };
|
|
1903
|
+
}
|
|
1904
|
+
function recordMcpStopped() {
|
|
1905
|
+
status = { running: false, port: null, error: null };
|
|
1137
1906
|
}
|
|
1138
1907
|
|
|
1139
1908
|
// src/main/ipc/settings.ts
|
|
1140
|
-
import { app as app7, ipcMain as ipcMain7 } from "electron";
|
|
1141
1909
|
function registerSettingsIpc(ctx) {
|
|
1142
|
-
|
|
1910
|
+
return new IpcRegistry(ctx.senderPolicy).handle(WorkbenchSettingsChannel.Get, () => {
|
|
1143
1911
|
return loadWorkbenchSettings();
|
|
1144
|
-
})
|
|
1145
|
-
|
|
1912
|
+
}).handle(WorkbenchSettingsChannel.Save, (_, ...args) => {
|
|
1913
|
+
const [settings] = validate(
|
|
1914
|
+
WorkbenchSettingsChannel.Save,
|
|
1915
|
+
WorkbenchSettingsSaveSchema,
|
|
1916
|
+
args
|
|
1917
|
+
);
|
|
1146
1918
|
saveWorkbenchSettings(settings);
|
|
1147
1919
|
applyTheme(settings.theme ?? "system");
|
|
1148
1920
|
return { success: true };
|
|
1149
|
-
})
|
|
1150
|
-
|
|
1921
|
+
}).handle(WorkbenchSettingsChannel.SetTheme, (_, ...args) => {
|
|
1922
|
+
const [theme] = validate(
|
|
1923
|
+
WorkbenchSettingsChannel.SetTheme,
|
|
1924
|
+
WorkbenchSettingsSetThemeSchema,
|
|
1925
|
+
args
|
|
1926
|
+
);
|
|
1151
1927
|
applyTheme(theme);
|
|
1152
|
-
})
|
|
1153
|
-
ipcMain7.handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
|
|
1928
|
+
}).handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
|
|
1154
1929
|
const settings = loadWorkbenchSettings();
|
|
1155
|
-
const switchValue =
|
|
1156
|
-
const implicitDevDefault = !
|
|
1930
|
+
const switchValue = app9.commandLine.getSwitchValue("remote-debugging-port");
|
|
1931
|
+
const implicitDevDefault = !app9.isPackaged && !settings.cdp.enabled && switchValue === String(DEFAULT_CDP_PORT);
|
|
1157
1932
|
return {
|
|
1158
1933
|
configured: settings.cdp.enabled,
|
|
1159
1934
|
port: settings.cdp.port,
|
|
@@ -1161,62 +1936,94 @@ function registerSettingsIpc(ctx) {
|
|
|
1161
1936
|
activePort: switchValue ? parseInt(switchValue, 10) : null,
|
|
1162
1937
|
implicitDevDefault
|
|
1163
1938
|
};
|
|
1164
|
-
})
|
|
1165
|
-
|
|
1939
|
+
}).handle(WorkbenchSettingsChannel.GetMcpStatus, () => {
|
|
1940
|
+
const settings = loadWorkbenchSettings();
|
|
1941
|
+
const runtime = getMcpStatus();
|
|
1942
|
+
return {
|
|
1943
|
+
configured: settings.mcp.enabled,
|
|
1944
|
+
configuredPort: settings.mcp.port,
|
|
1945
|
+
running: runtime.running,
|
|
1946
|
+
activePort: runtime.port,
|
|
1947
|
+
error: runtime.error
|
|
1948
|
+
};
|
|
1949
|
+
}).handle(WorkbenchSettingsChannel.SetVisible, async (_, ...args) => {
|
|
1950
|
+
const [visible] = validate(
|
|
1951
|
+
WorkbenchSettingsChannel.SetVisible,
|
|
1952
|
+
WorkbenchSettingsSetVisibleSchema,
|
|
1953
|
+
args
|
|
1954
|
+
);
|
|
1166
1955
|
if (visible) {
|
|
1167
1956
|
await openSettingsWindow(ctx);
|
|
1168
1957
|
} else {
|
|
1169
|
-
ctx.
|
|
1958
|
+
ctx.windows.closeSettingsWindow();
|
|
1170
1959
|
}
|
|
1171
|
-
})
|
|
1172
|
-
|
|
1960
|
+
}).handle(SettingsChannel.SetVisible, async (_, ...args) => {
|
|
1961
|
+
const [visible] = validate(SettingsChannel.SetVisible, SettingsSetVisibleSchema, args);
|
|
1173
1962
|
if (visible) {
|
|
1174
1963
|
await ctx.views.showSettings();
|
|
1964
|
+
const projectPath = ctx.workspace.getProjectPath();
|
|
1175
1965
|
ctx.notify.settingsInit({
|
|
1176
|
-
projectPath
|
|
1177
|
-
config: ctx.workspace.getCompileConfig(
|
|
1178
|
-
projectSettings: ctx.workspace.getProjectSettings(
|
|
1966
|
+
projectPath,
|
|
1967
|
+
config: await ctx.workspace.getCompileConfig(projectPath),
|
|
1968
|
+
projectSettings: ctx.workspace.getProjectSettings(projectPath)
|
|
1179
1969
|
});
|
|
1180
1970
|
} else {
|
|
1181
1971
|
ctx.views.hideSettings();
|
|
1182
1972
|
ctx.notify.settingsClosed();
|
|
1183
1973
|
}
|
|
1184
|
-
})
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1974
|
+
}).on(SettingsChannel.ConfigChanged, async (_, ...args) => {
|
|
1975
|
+
const [config] = validate(
|
|
1976
|
+
SettingsChannel.ConfigChanged,
|
|
1977
|
+
SettingsConfigChangedSchema,
|
|
1978
|
+
args
|
|
1979
|
+
);
|
|
1980
|
+
const projectPath = ctx.workspace.getProjectPath();
|
|
1981
|
+
if (projectPath) {
|
|
1982
|
+
await ctx.workspace.saveCompileConfig(projectPath, config);
|
|
1188
1983
|
}
|
|
1189
1984
|
ctx.notify.settingsChanged(config);
|
|
1985
|
+
}).on(SettingsChannel.ProjectSettingsChanged, (_, ...args) => {
|
|
1986
|
+
const [patch] = validate(
|
|
1987
|
+
SettingsChannel.ProjectSettingsChanged,
|
|
1988
|
+
SettingsProjectSettingsChangedSchema,
|
|
1989
|
+
args
|
|
1990
|
+
);
|
|
1991
|
+
ctx.workspace.updateProjectSettings(
|
|
1992
|
+
ctx.workspace.getProjectPath(),
|
|
1993
|
+
patch
|
|
1994
|
+
);
|
|
1190
1995
|
});
|
|
1191
|
-
ipcMain7.on(
|
|
1192
|
-
SettingsChannel.ProjectSettingsChanged,
|
|
1193
|
-
(_, patch) => {
|
|
1194
|
-
ctx.workspace.updateProjectSettings(ctx.workspace.getProjectPath(), patch);
|
|
1195
|
-
}
|
|
1196
|
-
);
|
|
1197
1996
|
}
|
|
1997
|
+
var settingsModule = {
|
|
1998
|
+
setup: (ctx) => registerSettingsIpc(ctx)
|
|
1999
|
+
};
|
|
1198
2000
|
|
|
1199
2001
|
// src/main/ipc/toolbar.ts
|
|
1200
|
-
import { ipcMain as ipcMain8 } from "electron";
|
|
1201
2002
|
function registerToolbarIpc(ctx) {
|
|
1202
|
-
|
|
2003
|
+
return new IpcRegistry(ctx.senderPolicy).handle(ToolbarChannel.GetActions, async () => {
|
|
1203
2004
|
if (ctx.toolbarActions) return ctx.toolbarActions();
|
|
1204
2005
|
return [];
|
|
1205
2006
|
});
|
|
1206
2007
|
}
|
|
1207
2008
|
|
|
2009
|
+
// src/main/ipc/simulator-module.ts
|
|
2010
|
+
var simulatorModule = {
|
|
2011
|
+
setup: (ctx) => {
|
|
2012
|
+
const reg = new DisposableRegistry();
|
|
2013
|
+
reg.add(registerSimulatorIpc(ctx));
|
|
2014
|
+
reg.add(registerPanelsIpc(ctx));
|
|
2015
|
+
reg.add(registerToolbarIpc(ctx));
|
|
2016
|
+
return reg;
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
|
|
1208
2020
|
// src/main/services/automation/index.ts
|
|
1209
|
-
import { ipcMain as ipcMain9 } from "electron";
|
|
1210
2021
|
import { WebSocketServer } from "ws";
|
|
1211
2022
|
|
|
1212
2023
|
// src/main/services/automation/exec.ts
|
|
1213
|
-
import { webContents as webContents3 } from "electron";
|
|
1214
2024
|
function getSimulator(ctx) {
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
const sim = webContents3.fromId(simWcId);
|
|
1218
|
-
if (!sim || sim.isDestroyed()) return null;
|
|
1219
|
-
return sim;
|
|
2025
|
+
if (!ctx.workspace.hasActiveSession()) return null;
|
|
2026
|
+
return ctx.views.getSimulatorWebContents();
|
|
1220
2027
|
}
|
|
1221
2028
|
async function evalInSim(ctx, expression) {
|
|
1222
2029
|
const sim = getSimulator(ctx);
|
|
@@ -1224,8 +2031,11 @@ async function evalInSim(ctx, expression) {
|
|
|
1224
2031
|
return sim.executeJavaScript(expression);
|
|
1225
2032
|
}
|
|
1226
2033
|
function buildElAccess(ref, varName = "el") {
|
|
1227
|
-
const
|
|
1228
|
-
|
|
2034
|
+
return `const ${varName} = _doc.querySelectorAll(${JSON.stringify(ref.selector)})[${ref.index}]`;
|
|
2035
|
+
}
|
|
2036
|
+
function evalInElement(ctx, ref, body, varName = "el") {
|
|
2037
|
+
return evalInSim(ctx, inIframe(`${buildElAccess(ref, varName)}
|
|
2038
|
+
${body}`));
|
|
1229
2039
|
}
|
|
1230
2040
|
function inIframe(code) {
|
|
1231
2041
|
return `(() => {
|
|
@@ -1245,47 +2055,57 @@ toolHandlers["Tool.close"] = async (ctx) => {
|
|
|
1245
2055
|
return {};
|
|
1246
2056
|
};
|
|
1247
2057
|
|
|
2058
|
+
// src/shared/simulator-route.ts
|
|
2059
|
+
function decodePageSpec(value) {
|
|
2060
|
+
const qIdx = value.indexOf("?");
|
|
2061
|
+
if (qIdx < 0) return { pagePath: value, query: {} };
|
|
2062
|
+
const pagePath = value.slice(0, qIdx);
|
|
2063
|
+
const query = {};
|
|
2064
|
+
for (const pair of value.slice(qIdx + 1).split("&")) {
|
|
2065
|
+
if (!pair) continue;
|
|
2066
|
+
const eqIdx = pair.indexOf("=");
|
|
2067
|
+
const k = eqIdx >= 0 ? pair.slice(0, eqIdx) : pair;
|
|
2068
|
+
const v = eqIdx >= 0 ? pair.slice(eqIdx + 1) : "";
|
|
2069
|
+
if (k) query[decodeURIComponent(k)] = decodeURIComponent(v);
|
|
2070
|
+
}
|
|
2071
|
+
return { pagePath, query };
|
|
2072
|
+
}
|
|
2073
|
+
function parseLocationRoute(search) {
|
|
2074
|
+
const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
|
|
2075
|
+
const appId = params.get("appId");
|
|
2076
|
+
const entryRaw = params.get("entry");
|
|
2077
|
+
if (!appId || !entryRaw) return null;
|
|
2078
|
+
const entry = decodePageSpec(entryRaw);
|
|
2079
|
+
const pageRaw = params.get("page");
|
|
2080
|
+
const current = pageRaw && pageRaw !== entryRaw ? decodePageSpec(pageRaw) : entry;
|
|
2081
|
+
return { appId, entry, current };
|
|
2082
|
+
}
|
|
2083
|
+
|
|
1248
2084
|
// src/main/services/automation/handlers/app.ts
|
|
1249
2085
|
var appHandlers = {};
|
|
2086
|
+
async function readRoute(ctx) {
|
|
2087
|
+
const { search } = await evalInSim(
|
|
2088
|
+
ctx,
|
|
2089
|
+
`({ search: location.search })`
|
|
2090
|
+
);
|
|
2091
|
+
return parseLocationRoute(search);
|
|
2092
|
+
}
|
|
1250
2093
|
appHandlers["App.getCurrentPage"] = async (ctx) => {
|
|
1251
|
-
const
|
|
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
|
-
}
|
|
2094
|
+
const route = await readRoute(ctx);
|
|
1271
2095
|
const iframeCount = await evalInSim(ctx, `document.querySelectorAll('iframe').length`);
|
|
1272
|
-
return {
|
|
2096
|
+
return {
|
|
2097
|
+
pageId: iframeCount,
|
|
2098
|
+
path: route?.current.pagePath ?? "",
|
|
2099
|
+
query: route?.current.query ?? {}
|
|
2100
|
+
};
|
|
1273
2101
|
};
|
|
1274
2102
|
appHandlers["App.getPageStack"] = async (ctx) => {
|
|
1275
|
-
const
|
|
1276
|
-
const clean = hash.replace(/^#/, "").replace(/\?.*$/, "");
|
|
2103
|
+
const route = await readRoute(ctx);
|
|
1277
2104
|
const pageStack = [];
|
|
1278
|
-
if (
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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
|
-
}
|
|
2105
|
+
if (!route) return { pageStack };
|
|
2106
|
+
pageStack.push({ pageId: 1, path: route.entry.pagePath, query: route.entry.query });
|
|
2107
|
+
if (route.current.pagePath !== route.entry.pagePath) {
|
|
2108
|
+
pageStack.push({ pageId: 2, path: route.current.pagePath, query: route.current.query });
|
|
1289
2109
|
}
|
|
1290
2110
|
return { pageStack };
|
|
1291
2111
|
};
|
|
@@ -1298,18 +2118,29 @@ appHandlers["App.callWxMethod"] = async (ctx, params) => {
|
|
|
1298
2118
|
if (url) {
|
|
1299
2119
|
const cleanUrl = url.startsWith("/") ? url : `/${url}`;
|
|
1300
2120
|
const clicked = await evalInSim(ctx, inIframe(`
|
|
1301
|
-
const
|
|
2121
|
+
const path = ${JSON.stringify(cleanUrl)}
|
|
2122
|
+
const el = Array.from(_doc.querySelectorAll('[data-path]'))
|
|
2123
|
+
.find(e => e.getAttribute('data-path') === path)
|
|
1302
2124
|
if (el) { el.click(); return true }
|
|
1303
2125
|
return false
|
|
1304
2126
|
`)).catch(() => false);
|
|
1305
2127
|
if (!clicked) {
|
|
1306
|
-
const
|
|
1307
|
-
const
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
2128
|
+
const apiName = JSON.stringify(method);
|
|
2129
|
+
const urlJson = JSON.stringify(cleanUrl);
|
|
2130
|
+
await evalInSim(
|
|
2131
|
+
ctx,
|
|
2132
|
+
`(() => {
|
|
2133
|
+
const iframes = Array.from(document.querySelectorAll('iframe'))
|
|
2134
|
+
for (const f of iframes) {
|
|
2135
|
+
try {
|
|
2136
|
+
if (f.contentWindow && f.contentWindow.wx && typeof f.contentWindow.wx[${apiName}] === 'function') {
|
|
2137
|
+
f.contentWindow.wx[${apiName}]({ url: ${urlJson} })
|
|
2138
|
+
return
|
|
2139
|
+
}
|
|
2140
|
+
} catch (_) {}
|
|
2141
|
+
}
|
|
2142
|
+
})()`
|
|
2143
|
+
);
|
|
1313
2144
|
}
|
|
1314
2145
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
1315
2146
|
}
|
|
@@ -1444,109 +2275,97 @@ pageHandlers["Page.getWindowProperties"] = async (ctx, params) => {
|
|
|
1444
2275
|
var elementHandlers = {};
|
|
1445
2276
|
elementHandlers["Element.tap"] = async (ctx, params) => {
|
|
1446
2277
|
const ref = getElementRef(params);
|
|
1447
|
-
await
|
|
1448
|
-
${buildElAccess(ref)}
|
|
2278
|
+
await evalInElement(ctx, ref, `
|
|
1449
2279
|
if (!el) throw new Error('Element not found')
|
|
1450
2280
|
el.click()
|
|
1451
|
-
`)
|
|
2281
|
+
`);
|
|
1452
2282
|
return {};
|
|
1453
2283
|
};
|
|
1454
2284
|
elementHandlers["Element.triggerEvent"] = async (ctx, params) => {
|
|
1455
2285
|
const ref = getElementRef(params);
|
|
1456
2286
|
const type = params.type;
|
|
1457
2287
|
const detail = params.detail || {};
|
|
1458
|
-
await
|
|
1459
|
-
${buildElAccess(ref)}
|
|
2288
|
+
await evalInElement(ctx, ref, `
|
|
1460
2289
|
if (!el) throw new Error('Element not found')
|
|
1461
2290
|
el.dispatchEvent(new CustomEvent('${type}', { detail: ${JSON.stringify(detail)}, bubbles: true }))
|
|
1462
|
-
`)
|
|
2291
|
+
`);
|
|
1463
2292
|
return {};
|
|
1464
2293
|
};
|
|
1465
2294
|
elementHandlers["Element.touchstart"] = async (ctx, params) => {
|
|
1466
2295
|
const ref = getElementRef(params);
|
|
1467
|
-
await
|
|
1468
|
-
${buildElAccess(ref)}
|
|
2296
|
+
await evalInElement(ctx, ref, `
|
|
1469
2297
|
if (el) el.dispatchEvent(new TouchEvent('touchstart', { bubbles: true }))
|
|
1470
|
-
`)
|
|
2298
|
+
`);
|
|
1471
2299
|
return {};
|
|
1472
2300
|
};
|
|
1473
2301
|
elementHandlers["Element.touchmove"] = async (ctx, params) => {
|
|
1474
2302
|
const ref = getElementRef(params);
|
|
1475
|
-
await
|
|
1476
|
-
${buildElAccess(ref)}
|
|
2303
|
+
await evalInElement(ctx, ref, `
|
|
1477
2304
|
if (el) el.dispatchEvent(new TouchEvent('touchmove', { bubbles: true }))
|
|
1478
|
-
`)
|
|
2305
|
+
`);
|
|
1479
2306
|
return {};
|
|
1480
2307
|
};
|
|
1481
2308
|
elementHandlers["Element.touchend"] = async (ctx, params) => {
|
|
1482
2309
|
const ref = getElementRef(params);
|
|
1483
|
-
await
|
|
1484
|
-
${buildElAccess(ref)}
|
|
2310
|
+
await evalInElement(ctx, ref, `
|
|
1485
2311
|
if (el) el.dispatchEvent(new TouchEvent('touchend', { bubbles: true }))
|
|
1486
|
-
`)
|
|
2312
|
+
`);
|
|
1487
2313
|
return {};
|
|
1488
2314
|
};
|
|
1489
2315
|
elementHandlers["Element.getDOMProperties"] = async (ctx, params) => {
|
|
1490
2316
|
const ref = getElementRef(params);
|
|
1491
2317
|
const names = params.names;
|
|
1492
|
-
const properties = await
|
|
1493
|
-
${buildElAccess(ref)}
|
|
2318
|
+
const properties = await evalInElement(ctx, ref, `
|
|
1494
2319
|
if (!el) return ${JSON.stringify(names.map(() => null))}
|
|
1495
2320
|
return ${JSON.stringify(names)}.map(n => el[n])
|
|
1496
|
-
`)
|
|
2321
|
+
`);
|
|
1497
2322
|
return { properties };
|
|
1498
2323
|
};
|
|
1499
2324
|
elementHandlers["Element.getAttributes"] = async (ctx, params) => {
|
|
1500
2325
|
const ref = getElementRef(params);
|
|
1501
2326
|
const names = params.names;
|
|
1502
|
-
const attributes = await
|
|
1503
|
-
${buildElAccess(ref)}
|
|
2327
|
+
const attributes = await evalInElement(ctx, ref, `
|
|
1504
2328
|
if (!el) return ${JSON.stringify(names.map(() => null))}
|
|
1505
2329
|
return ${JSON.stringify(names)}.map(n => el.getAttribute(n))
|
|
1506
|
-
`)
|
|
2330
|
+
`);
|
|
1507
2331
|
return { attributes };
|
|
1508
2332
|
};
|
|
1509
2333
|
elementHandlers["Element.getStyles"] = async (ctx, params) => {
|
|
1510
2334
|
const ref = getElementRef(params);
|
|
1511
2335
|
const names = params.names;
|
|
1512
|
-
const styles = await
|
|
1513
|
-
${buildElAccess(ref)}
|
|
2336
|
+
const styles = await evalInElement(ctx, ref, `
|
|
1514
2337
|
if (!el) return ${JSON.stringify(names.map(() => ""))}
|
|
1515
2338
|
const cs = getComputedStyle(el)
|
|
1516
2339
|
return ${JSON.stringify(names)}.map(n => cs.getPropertyValue(n))
|
|
1517
|
-
`)
|
|
2340
|
+
`);
|
|
1518
2341
|
return { styles };
|
|
1519
2342
|
};
|
|
1520
2343
|
elementHandlers["Element.getWXML"] = async (ctx, params) => {
|
|
1521
2344
|
const ref = getElementRef(params);
|
|
1522
2345
|
const type = params.type || "inner";
|
|
1523
2346
|
const prop = type === "outer" ? "outerHTML" : "innerHTML";
|
|
1524
|
-
const wxml = await
|
|
1525
|
-
${buildElAccess(ref)}
|
|
2347
|
+
const wxml = await evalInElement(ctx, ref, `
|
|
1526
2348
|
return el ? el.${prop} : ''
|
|
1527
|
-
`)
|
|
2349
|
+
`);
|
|
1528
2350
|
return { wxml };
|
|
1529
2351
|
};
|
|
1530
2352
|
elementHandlers["Element.getOffset"] = async (ctx, params) => {
|
|
1531
2353
|
const ref = getElementRef(params);
|
|
1532
|
-
return
|
|
1533
|
-
${buildElAccess(ref)}
|
|
2354
|
+
return evalInElement(ctx, ref, `
|
|
1534
2355
|
if (!el) return { left: 0, top: 0 }
|
|
1535
2356
|
const r = el.getBoundingClientRect()
|
|
1536
2357
|
return { left: r.left, top: r.top }
|
|
1537
|
-
`)
|
|
2358
|
+
`);
|
|
1538
2359
|
};
|
|
1539
2360
|
elementHandlers["Element.getElement"] = async (ctx, params) => {
|
|
1540
2361
|
const parentRef = getElementRef(params);
|
|
1541
2362
|
const selector = params.selector;
|
|
1542
|
-
const
|
|
1543
|
-
const info = await evalInSim(ctx, inIframe(`
|
|
1544
|
-
${buildElAccess(parentRef, "parent")}
|
|
2363
|
+
const info = await evalInElement(ctx, parentRef, `
|
|
1545
2364
|
if (!parent) return null
|
|
1546
|
-
const child = parent.querySelector(
|
|
2365
|
+
const child = parent.querySelector(${JSON.stringify(selector)})
|
|
1547
2366
|
if (!child) return null
|
|
1548
2367
|
return { tagName: child.tagName.toLowerCase() }
|
|
1549
|
-
|
|
2368
|
+
`, "parent");
|
|
1550
2369
|
if (!info) throw new Error(`Child element not found: ${selector}`);
|
|
1551
2370
|
const combinedSelector = `${parentRef.selector} ${selector}`;
|
|
1552
2371
|
const elementId = registerElement(combinedSelector, 0, parentRef.pageId);
|
|
@@ -1555,14 +2374,12 @@ elementHandlers["Element.getElement"] = async (ctx, params) => {
|
|
|
1555
2374
|
elementHandlers["Element.getElements"] = async (ctx, params) => {
|
|
1556
2375
|
const parentRef = getElementRef(params);
|
|
1557
2376
|
const selector = params.selector;
|
|
1558
|
-
const
|
|
1559
|
-
const items = await evalInSim(ctx, inIframe(`
|
|
1560
|
-
${buildElAccess(parentRef, "parent")}
|
|
2377
|
+
const items = await evalInElement(ctx, parentRef, `
|
|
1561
2378
|
if (!parent) return []
|
|
1562
|
-
return Array.from(parent.querySelectorAll(
|
|
2379
|
+
return Array.from(parent.querySelectorAll(${JSON.stringify(selector)})).map(el => ({
|
|
1563
2380
|
tagName: el.tagName.toLowerCase(),
|
|
1564
2381
|
}))
|
|
1565
|
-
|
|
2382
|
+
`, "parent");
|
|
1566
2383
|
const combinedSelector = `${parentRef.selector} ${selector}`;
|
|
1567
2384
|
return {
|
|
1568
2385
|
elements: items.map((item, i) => ({
|
|
@@ -1595,7 +2412,6 @@ var handlers = {
|
|
|
1595
2412
|
...elementHandlers
|
|
1596
2413
|
};
|
|
1597
2414
|
var currentPort = null;
|
|
1598
|
-
var portIpcRegistered = false;
|
|
1599
2415
|
async function startAutomationServer(ctx, port = 0) {
|
|
1600
2416
|
const wss = new WebSocketServer({ port });
|
|
1601
2417
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -1606,10 +2422,10 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
1606
2422
|
const addr = wss.address();
|
|
1607
2423
|
const resolvedPort = typeof addr === "object" && addr ? addr.port : port;
|
|
1608
2424
|
currentPort = resolvedPort;
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
}
|
|
2425
|
+
const portIpc = new IpcRegistry(ctx.senderPolicy);
|
|
2426
|
+
portIpc.handle(AutomationChannel.GetPort, () => {
|
|
2427
|
+
return currentPort;
|
|
2428
|
+
});
|
|
1613
2429
|
function broadcast(event) {
|
|
1614
2430
|
const msg = JSON.stringify(event);
|
|
1615
2431
|
for (const ws of clients) {
|
|
@@ -1617,14 +2433,46 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
1617
2433
|
}
|
|
1618
2434
|
}
|
|
1619
2435
|
let consoleForwardingSetup = false;
|
|
2436
|
+
let pollInterval = null;
|
|
2437
|
+
let pollStopTimer = null;
|
|
2438
|
+
let attachedSim = null;
|
|
2439
|
+
let ipcMessageHostHandler = null;
|
|
2440
|
+
let simDestroyedHandler = null;
|
|
2441
|
+
function detachConsoleForwarding() {
|
|
2442
|
+
if (attachedSim) {
|
|
2443
|
+
if (ipcMessageHostHandler) {
|
|
2444
|
+
try {
|
|
2445
|
+
if (!attachedSim.isDestroyed()) {
|
|
2446
|
+
;
|
|
2447
|
+
attachedSim.removeListener("ipc-message-host", ipcMessageHostHandler);
|
|
2448
|
+
}
|
|
2449
|
+
} catch {
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
if (simDestroyedHandler) {
|
|
2453
|
+
try {
|
|
2454
|
+
if (!attachedSim.isDestroyed()) {
|
|
2455
|
+
attachedSim.removeListener("destroyed", simDestroyedHandler);
|
|
2456
|
+
}
|
|
2457
|
+
} catch {
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
attachedSim = null;
|
|
2462
|
+
ipcMessageHostHandler = null;
|
|
2463
|
+
simDestroyedHandler = null;
|
|
2464
|
+
}
|
|
1620
2465
|
function setupConsoleForwarding() {
|
|
1621
2466
|
if (consoleForwardingSetup) return;
|
|
1622
2467
|
consoleForwardingSetup = true;
|
|
1623
|
-
|
|
2468
|
+
pollInterval = setInterval(() => {
|
|
1624
2469
|
const sim = getSimulator(ctx);
|
|
1625
2470
|
if (!sim) return;
|
|
1626
|
-
|
|
1627
|
-
|
|
2471
|
+
if (pollInterval) {
|
|
2472
|
+
clearInterval(pollInterval);
|
|
2473
|
+
pollInterval = null;
|
|
2474
|
+
}
|
|
2475
|
+
const onIpcMessageHost = (_event, channel, data) => {
|
|
1628
2476
|
if (channel === SimulatorChannel.Console) {
|
|
1629
2477
|
const logData = data;
|
|
1630
2478
|
broadcast({
|
|
@@ -1632,9 +2480,26 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
1632
2480
|
params: { type: logData.level || "log", args: logData.args || [] }
|
|
1633
2481
|
});
|
|
1634
2482
|
}
|
|
1635
|
-
}
|
|
2483
|
+
};
|
|
2484
|
+
const onSimDestroyed = () => {
|
|
2485
|
+
attachedSim = null;
|
|
2486
|
+
ipcMessageHostHandler = null;
|
|
2487
|
+
simDestroyedHandler = null;
|
|
2488
|
+
consoleForwardingSetup = false;
|
|
2489
|
+
};
|
|
2490
|
+
attachedSim = sim;
|
|
2491
|
+
ipcMessageHostHandler = onIpcMessageHost;
|
|
2492
|
+
simDestroyedHandler = onSimDestroyed;
|
|
2493
|
+
sim.on("ipc-message-host", onIpcMessageHost);
|
|
2494
|
+
sim.once("destroyed", onSimDestroyed);
|
|
1636
2495
|
}, 1e3);
|
|
1637
|
-
setTimeout(() =>
|
|
2496
|
+
pollStopTimer = setTimeout(() => {
|
|
2497
|
+
if (pollInterval) {
|
|
2498
|
+
clearInterval(pollInterval);
|
|
2499
|
+
pollInterval = null;
|
|
2500
|
+
}
|
|
2501
|
+
pollStopTimer = null;
|
|
2502
|
+
}, 3e4);
|
|
1638
2503
|
}
|
|
1639
2504
|
wss.on("connection", (ws) => {
|
|
1640
2505
|
clients.add(ws);
|
|
@@ -1665,7 +2530,24 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
1665
2530
|
});
|
|
1666
2531
|
return {
|
|
1667
2532
|
close: () => {
|
|
2533
|
+
if (pollInterval) {
|
|
2534
|
+
clearInterval(pollInterval);
|
|
2535
|
+
pollInterval = null;
|
|
2536
|
+
}
|
|
2537
|
+
if (pollStopTimer) {
|
|
2538
|
+
clearTimeout(pollStopTimer);
|
|
2539
|
+
pollStopTimer = null;
|
|
2540
|
+
}
|
|
2541
|
+
detachConsoleForwarding();
|
|
2542
|
+
for (const ws of clients) {
|
|
2543
|
+
try {
|
|
2544
|
+
ws.close();
|
|
2545
|
+
} catch {
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
clients.clear();
|
|
1668
2549
|
if (currentPort === resolvedPort) currentPort = null;
|
|
2550
|
+
void portIpc.dispose();
|
|
1669
2551
|
wss.close();
|
|
1670
2552
|
},
|
|
1671
2553
|
port: resolvedPort
|
|
@@ -1683,7 +2565,7 @@ import CDP from "chrome-remote-interface";
|
|
|
1683
2565
|
var SIMULATOR_URL_PATTERN = "localhost:7788";
|
|
1684
2566
|
var MAX_BUFFER = 500;
|
|
1685
2567
|
var RECONNECT_INTERVAL_MS = 3e3;
|
|
1686
|
-
var cdpPort =
|
|
2568
|
+
var cdpPort = DEFAULT_CDP_PORT;
|
|
1687
2569
|
var targets = {
|
|
1688
2570
|
simulator: { client: null, connected: false, timer: null, consoleLogs: [], networkRequests: [] },
|
|
1689
2571
|
workbench: { client: null, connected: false, timer: null, consoleLogs: [], networkRequests: [] }
|
|
@@ -1824,7 +2706,7 @@ function getClient(kind) {
|
|
|
1824
2706
|
}
|
|
1825
2707
|
|
|
1826
2708
|
// src/main/services/mcp/tool-registry.ts
|
|
1827
|
-
import { z } from "zod";
|
|
2709
|
+
import { z as z2 } from "zod";
|
|
1828
2710
|
function registerCommonTargetTools(server, kind) {
|
|
1829
2711
|
const descriptions = kind === "simulator" ? {
|
|
1830
2712
|
screenshot: "Take a screenshot of the simulator webview",
|
|
@@ -1845,9 +2727,9 @@ function registerCommonTargetTools(server, kind) {
|
|
|
1845
2727
|
return { content: [{ type: "image", data, mimeType: "image/png" }] };
|
|
1846
2728
|
});
|
|
1847
2729
|
server.tool(`${kind}_console_logs`, descriptions.consoleLogs, {
|
|
1848
|
-
limit:
|
|
1849
|
-
level:
|
|
1850
|
-
sinceTimestamp:
|
|
2730
|
+
limit: z2.number().optional().default(50).describe("Maximum number of log entries"),
|
|
2731
|
+
level: z2.enum(["error", "warning", "warn", "info", "log", "debug"]).optional().describe("Only return entries with this level"),
|
|
2732
|
+
sinceTimestamp: z2.string().optional().describe("ISO timestamp; only return entries at or after this time")
|
|
1851
2733
|
}, async ({ limit, level, sinceTimestamp }) => {
|
|
1852
2734
|
getClient(kind);
|
|
1853
2735
|
let entries = getTargetState(kind).consoleLogs;
|
|
@@ -1862,7 +2744,7 @@ function registerCommonTargetTools(server, kind) {
|
|
|
1862
2744
|
});
|
|
1863
2745
|
if (kind !== "workbench") {
|
|
1864
2746
|
server.tool(`${kind}_evaluate`, descriptions.evaluate, {
|
|
1865
|
-
expression:
|
|
2747
|
+
expression: z2.string().describe("JavaScript expression to evaluate")
|
|
1866
2748
|
}, async ({ expression }) => {
|
|
1867
2749
|
const c = getClient(kind);
|
|
1868
2750
|
const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
|
|
@@ -1875,15 +2757,15 @@ ${JSON.stringify(result.exceptionDetails, null, 2)}` }], isError: true };
|
|
|
1875
2757
|
});
|
|
1876
2758
|
}
|
|
1877
2759
|
server.tool(`${kind}_get_dom`, descriptions.getDom, {
|
|
1878
|
-
depth:
|
|
2760
|
+
depth: z2.number().optional().default(3).describe("Maximum depth")
|
|
1879
2761
|
}, async ({ depth }) => {
|
|
1880
2762
|
const c = getClient(kind);
|
|
1881
2763
|
const { root } = await c.DOM.getDocument({ depth });
|
|
1882
2764
|
return { content: [{ type: "text", text: JSON.stringify(root, null, 2) }] };
|
|
1883
2765
|
});
|
|
1884
2766
|
server.tool(`${kind}_network_log`, descriptions.networkLog, {
|
|
1885
|
-
limit:
|
|
1886
|
-
minStatus:
|
|
2767
|
+
limit: z2.number().optional().default(20).describe("Maximum number of entries"),
|
|
2768
|
+
minStatus: z2.number().optional().describe("Only return entries with status >= minStatus (status 0 = failed before response is included when minStatus >= 400)")
|
|
1887
2769
|
}, async ({ limit, minStatus }) => {
|
|
1888
2770
|
getClient(kind);
|
|
1889
2771
|
let entries = getTargetState(kind).networkRequests;
|
|
@@ -2126,11 +3008,11 @@ function registerContextTools(server) {
|
|
|
2126
3008
|
}
|
|
2127
3009
|
|
|
2128
3010
|
// src/main/services/mcp/tools/simulator-tools.ts
|
|
2129
|
-
import { z as
|
|
3011
|
+
import { z as z3 } from "zod";
|
|
2130
3012
|
function registerSimulatorTools(server) {
|
|
2131
3013
|
server.tool("simulator_navigate", "Navigate the simulator to a URL, or reload the current page", {
|
|
2132
|
-
url:
|
|
2133
|
-
reload:
|
|
3014
|
+
url: z3.string().optional().describe("URL to navigate to"),
|
|
3015
|
+
reload: z3.boolean().optional().describe("If true, reload current page instead of navigating")
|
|
2134
3016
|
}, async ({ url, reload }) => {
|
|
2135
3017
|
const c = getClient("simulator");
|
|
2136
3018
|
if (reload) {
|
|
@@ -2145,15 +3027,15 @@ function registerSimulatorTools(server) {
|
|
|
2145
3027
|
return { content: [{ type: "text", text: "Error: either url or reload is required" }], isError: true };
|
|
2146
3028
|
});
|
|
2147
3029
|
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:
|
|
2149
|
-
x:
|
|
2150
|
-
y:
|
|
2151
|
-
selector:
|
|
2152
|
-
nth:
|
|
2153
|
-
text:
|
|
2154
|
-
key:
|
|
2155
|
-
deltaX:
|
|
2156
|
-
deltaY:
|
|
3030
|
+
action: z3.enum(["tap_coord", "tap_selector", "type", "scroll", "key"]).describe("Input action to dispatch"),
|
|
3031
|
+
x: z3.number().optional().describe("Viewport x coordinate for `tap_coord` or `scroll`"),
|
|
3032
|
+
y: z3.number().optional().describe("Viewport y coordinate for `tap_coord` or `scroll`"),
|
|
3033
|
+
selector: z3.string().optional().describe("CSS selector for `tap_selector` or `type`"),
|
|
3034
|
+
nth: z3.number().int().optional().describe("Zero-based index when selector matches multiple elements; defaults to 0"),
|
|
3035
|
+
text: z3.string().optional().describe("Text to insert for `type`"),
|
|
3036
|
+
key: z3.string().optional().describe("Best-effort keyboard key name for `key`; common keys such as Enter, Escape, Tab, Arrow* and Space work best"),
|
|
3037
|
+
deltaX: z3.number().optional().describe("Horizontal wheel delta for `scroll`"),
|
|
3038
|
+
deltaY: z3.number().optional().describe("Vertical wheel delta for `scroll`")
|
|
2157
3039
|
}, async ({ action, x, y, selector, nth, text, key, deltaX, deltaY }) => {
|
|
2158
3040
|
const c = getClient("simulator");
|
|
2159
3041
|
const err = (msg) => ({ content: [{ type: "text", text: `Error: ${msg}` }], isError: true });
|
|
@@ -2301,94 +3183,280 @@ function startMcpServer(resolvedCdpPort, mcpPort) {
|
|
|
2301
3183
|
httpServer.on("error", (err) => {
|
|
2302
3184
|
if (err.code === "EADDRINUSE") {
|
|
2303
3185
|
console.warn(`[MCP] Port ${mcpPort} already in use \u2014 MCP server not started`);
|
|
3186
|
+
recordMcpFailed("port-in-use");
|
|
2304
3187
|
} else {
|
|
2305
3188
|
console.error("[MCP] Server error:", err);
|
|
3189
|
+
recordMcpFailed(err.message);
|
|
2306
3190
|
}
|
|
2307
3191
|
});
|
|
2308
3192
|
httpServer.listen(mcpPort, "127.0.0.1", () => {
|
|
2309
3193
|
console.log(`[MCP] SSE server listening on http://127.0.0.1:${mcpPort}/sse`);
|
|
3194
|
+
recordMcpStarted(mcpPort);
|
|
3195
|
+
});
|
|
3196
|
+
return toDisposable(() => {
|
|
3197
|
+
transports.clear();
|
|
3198
|
+
httpServer.close();
|
|
3199
|
+
recordMcpStopped();
|
|
2310
3200
|
});
|
|
2311
3201
|
}
|
|
2312
3202
|
|
|
2313
3203
|
// src/main/services/simulator-storage/index.ts
|
|
2314
|
-
import { app as
|
|
2315
|
-
var attachedWc = null;
|
|
2316
|
-
var hostWc = null;
|
|
3204
|
+
import { app as app10, webContents as wcStatic } from "electron";
|
|
2317
3205
|
function isSimulatorWebview(wc) {
|
|
2318
3206
|
if (wc.isDestroyed()) return false;
|
|
2319
3207
|
if (wc.getType() !== "webview") return false;
|
|
2320
3208
|
return wc.getURL().includes("simulator.html");
|
|
2321
3209
|
}
|
|
2322
|
-
function
|
|
2323
|
-
if (!attachedWc) return;
|
|
2324
|
-
const wc = attachedWc;
|
|
2325
|
-
attachedWc = null;
|
|
3210
|
+
function safeOff(target, event, fn) {
|
|
2326
3211
|
try {
|
|
2327
|
-
if (
|
|
2328
|
-
|
|
2329
|
-
}
|
|
3212
|
+
if (target.isDestroyed?.()) return;
|
|
3213
|
+
target.removeListener(event, fn);
|
|
2330
3214
|
} catch {
|
|
2331
3215
|
}
|
|
2332
3216
|
}
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
3217
|
+
function setupSimulatorStorage(host, options) {
|
|
3218
|
+
const registry = new DisposableRegistry();
|
|
3219
|
+
const getActiveAppId = options.getActiveAppId;
|
|
3220
|
+
let attachedWc = null;
|
|
3221
|
+
let attachDisposables = null;
|
|
3222
|
+
let cachedOrigin = null;
|
|
3223
|
+
function activePrefix() {
|
|
3224
|
+
const appId = getActiveAppId();
|
|
3225
|
+
return appId ? `${appId}_` : null;
|
|
3226
|
+
}
|
|
3227
|
+
function detachFromSim() {
|
|
3228
|
+
if (!attachedWc) return;
|
|
3229
|
+
const wc = attachedWc;
|
|
3230
|
+
attachedWc = null;
|
|
3231
|
+
cachedOrigin = null;
|
|
3232
|
+
const ad = attachDisposables;
|
|
3233
|
+
attachDisposables = null;
|
|
3234
|
+
if (ad) void ad.disposeAll().catch(() => {
|
|
3235
|
+
});
|
|
3236
|
+
try {
|
|
3237
|
+
if (!wc.isDestroyed() && wc.debugger.isAttached()) {
|
|
3238
|
+
wc.debugger.detach();
|
|
3239
|
+
}
|
|
3240
|
+
} catch {
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
async function attachToSim(wc) {
|
|
3244
|
+
if (attachedWc === wc) return;
|
|
3245
|
+
if (wc.isDestroyed()) return;
|
|
3246
|
+
detachFromSim();
|
|
3247
|
+
try {
|
|
3248
|
+
if (!wc.debugger.isAttached()) {
|
|
3249
|
+
wc.debugger.attach("1.3");
|
|
3250
|
+
}
|
|
3251
|
+
await wc.debugger.sendCommand("DOMStorage.enable");
|
|
3252
|
+
const attach = new DisposableRegistry();
|
|
3253
|
+
attachDisposables = attach;
|
|
3254
|
+
const onMessage = (_event, method, params) => forwardCdpMessage(method, params);
|
|
3255
|
+
wc.debugger.on("message", onMessage);
|
|
3256
|
+
attach.add(() => safeOff(wc.debugger, "message", onMessage));
|
|
3257
|
+
const onDetach = () => {
|
|
3258
|
+
if (attachedWc === wc) attachedWc = null;
|
|
3259
|
+
};
|
|
3260
|
+
wc.debugger.on("detach", onDetach);
|
|
3261
|
+
attach.add(() => safeOff(wc.debugger, "detach", onDetach));
|
|
3262
|
+
const onDestroyed = () => {
|
|
3263
|
+
if (attachedWc === wc) {
|
|
3264
|
+
attachedWc = null;
|
|
3265
|
+
const ad = attachDisposables;
|
|
3266
|
+
attachDisposables = null;
|
|
3267
|
+
if (ad) void ad.disposeAll().catch(() => {
|
|
3268
|
+
});
|
|
3269
|
+
}
|
|
3270
|
+
};
|
|
3271
|
+
wc.once("destroyed", onDestroyed);
|
|
3272
|
+
attach.add(() => safeOff(wc, "destroyed", onDestroyed));
|
|
3273
|
+
attachedWc = wc;
|
|
3274
|
+
} catch (e) {
|
|
3275
|
+
console.warn("[storage-watcher] attach failed:", e.message);
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
function forwardCdpMessage(method, params) {
|
|
3279
|
+
if (host.isDestroyed()) return;
|
|
3280
|
+
const p = params;
|
|
3281
|
+
const prefix = activePrefix();
|
|
3282
|
+
let evt;
|
|
3283
|
+
switch (method) {
|
|
3284
|
+
case "DOMStorage.domStorageItemAdded": {
|
|
3285
|
+
const key = String(p.key);
|
|
3286
|
+
if (prefix && !key.startsWith(prefix)) return;
|
|
3287
|
+
evt = { type: "added", key, newValue: String(p.newValue ?? "") };
|
|
3288
|
+
break;
|
|
3289
|
+
}
|
|
3290
|
+
case "DOMStorage.domStorageItemUpdated": {
|
|
3291
|
+
const key = String(p.key);
|
|
3292
|
+
if (prefix && !key.startsWith(prefix)) return;
|
|
3293
|
+
evt = {
|
|
3294
|
+
type: "updated",
|
|
3295
|
+
key,
|
|
3296
|
+
oldValue: String(p.oldValue ?? ""),
|
|
3297
|
+
newValue: String(p.newValue ?? "")
|
|
3298
|
+
};
|
|
3299
|
+
break;
|
|
3300
|
+
}
|
|
3301
|
+
case "DOMStorage.domStorageItemRemoved": {
|
|
3302
|
+
const key = String(p.key);
|
|
3303
|
+
if (prefix && !key.startsWith(prefix)) return;
|
|
3304
|
+
evt = { type: "removed", key };
|
|
3305
|
+
break;
|
|
3306
|
+
}
|
|
3307
|
+
case "DOMStorage.domStorageItemsCleared":
|
|
3308
|
+
evt = { type: "cleared" };
|
|
3309
|
+
break;
|
|
3310
|
+
default:
|
|
3311
|
+
return;
|
|
2340
3312
|
}
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
3313
|
+
host.send(SimulatorStorageChannel.Event, evt);
|
|
3314
|
+
}
|
|
3315
|
+
async function ensureAttached() {
|
|
3316
|
+
if (attachedWc && !attachedWc.isDestroyed()) return;
|
|
3317
|
+
const sim = findSimulatorWebContents();
|
|
3318
|
+
if (!sim) return;
|
|
3319
|
+
await attachToSim(sim);
|
|
3320
|
+
}
|
|
3321
|
+
async function getStorageId() {
|
|
3322
|
+
if (!attachedWc || attachedWc.isDestroyed()) return null;
|
|
3323
|
+
if (!cachedOrigin) {
|
|
3324
|
+
try {
|
|
3325
|
+
cachedOrigin = String(await attachedWc.executeJavaScript("location.origin"));
|
|
3326
|
+
} catch {
|
|
3327
|
+
return null;
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
return { securityOrigin: cachedOrigin, isLocalStorage: true };
|
|
3331
|
+
}
|
|
3332
|
+
async function withStorageWrite(fn) {
|
|
3333
|
+
await ensureAttached();
|
|
3334
|
+
if (!attachedWc || attachedWc.isDestroyed()) {
|
|
3335
|
+
return { ok: false, error: "simulator not attached" };
|
|
3336
|
+
}
|
|
3337
|
+
const storageId = await getStorageId();
|
|
3338
|
+
if (!storageId) return { ok: false, error: "failed to resolve simulator origin" };
|
|
3339
|
+
try {
|
|
3340
|
+
return await fn({ wc: attachedWc, storageId });
|
|
3341
|
+
} catch (e) {
|
|
3342
|
+
return { ok: false, error: e.message };
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
async function withStorageRead(fallback, fn) {
|
|
3346
|
+
await ensureAttached();
|
|
3347
|
+
if (!attachedWc || attachedWc.isDestroyed()) return fallback;
|
|
3348
|
+
try {
|
|
3349
|
+
const storageId = await getStorageId();
|
|
3350
|
+
if (!storageId) return fallback;
|
|
3351
|
+
return await fn({ wc: attachedWc, storageId });
|
|
3352
|
+
} catch {
|
|
3353
|
+
return fallback;
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
function pushSyntheticEvent(evt) {
|
|
3357
|
+
if (host.isDestroyed()) return;
|
|
3358
|
+
const prefix = activePrefix();
|
|
3359
|
+
if (prefix && !evt.key.startsWith(prefix)) return;
|
|
3360
|
+
host.send(SimulatorStorageChannel.Event, evt);
|
|
3361
|
+
}
|
|
3362
|
+
async function setItem(key, value) {
|
|
3363
|
+
return withStorageWrite(async ({ wc, storageId }) => {
|
|
3364
|
+
await wc.debugger.sendCommand("DOMStorage.setDOMStorageItem", { storageId, key, value });
|
|
3365
|
+
pushSyntheticEvent({ type: "added", key, newValue: value });
|
|
3366
|
+
return { ok: true };
|
|
2345
3367
|
});
|
|
2346
|
-
|
|
2347
|
-
|
|
3368
|
+
}
|
|
3369
|
+
async function removeItem(key) {
|
|
3370
|
+
return withStorageWrite(async ({ wc, storageId }) => {
|
|
3371
|
+
await wc.debugger.sendCommand("DOMStorage.removeDOMStorageItem", { storageId, key });
|
|
3372
|
+
pushSyntheticEvent({ type: "removed", key });
|
|
3373
|
+
return { ok: true };
|
|
2348
3374
|
});
|
|
2349
|
-
attachedWc = wc;
|
|
2350
|
-
} catch (e) {
|
|
2351
|
-
console.warn("[storage-watcher] attach failed:", e.message);
|
|
2352
3375
|
}
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
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;
|
|
3376
|
+
async function clearScoped() {
|
|
3377
|
+
return withStorageWrite(async ({ wc, storageId }) => {
|
|
3378
|
+
const prefix = activePrefix();
|
|
3379
|
+
if (!prefix) {
|
|
3380
|
+
await wc.debugger.sendCommand("DOMStorage.clear", { storageId });
|
|
3381
|
+
return { ok: true };
|
|
3382
|
+
}
|
|
3383
|
+
const result = await wc.debugger.sendCommand("DOMStorage.getDOMStorageItems", {
|
|
3384
|
+
storageId
|
|
3385
|
+
});
|
|
3386
|
+
for (const [key] of result.entries) {
|
|
3387
|
+
if (!key.startsWith(prefix)) continue;
|
|
3388
|
+
await wc.debugger.sendCommand("DOMStorage.removeDOMStorageItem", { storageId, key });
|
|
3389
|
+
}
|
|
3390
|
+
return { ok: true };
|
|
3391
|
+
});
|
|
2378
3392
|
}
|
|
2379
|
-
|
|
2380
|
-
}
|
|
2381
|
-
|
|
2382
|
-
|
|
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 }
|
|
3393
|
+
async function clearAll() {
|
|
3394
|
+
return withStorageWrite(async ({ wc, storageId }) => {
|
|
3395
|
+
await wc.debugger.sendCommand("DOMStorage.clear", { storageId });
|
|
3396
|
+
return { ok: true };
|
|
2387
3397
|
});
|
|
2388
|
-
return result.entries.map(([key, value]) => ({ key, value }));
|
|
2389
|
-
} catch {
|
|
2390
|
-
return [];
|
|
2391
3398
|
}
|
|
3399
|
+
async function getSnapshot() {
|
|
3400
|
+
return withStorageRead([], async ({ wc, storageId }) => {
|
|
3401
|
+
const result = await wc.debugger.sendCommand("DOMStorage.getDOMStorageItems", {
|
|
3402
|
+
storageId
|
|
3403
|
+
});
|
|
3404
|
+
const prefix = activePrefix();
|
|
3405
|
+
const filtered = prefix ? result.entries.filter(([key]) => key.startsWith(prefix)) : result.entries;
|
|
3406
|
+
return filtered.map(([key, value]) => ({ key, value }));
|
|
3407
|
+
});
|
|
3408
|
+
}
|
|
3409
|
+
for (const wc of wcStatic.getAllWebContents()) {
|
|
3410
|
+
if (isSimulatorWebview(wc)) {
|
|
3411
|
+
void attachToSim(wc);
|
|
3412
|
+
break;
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
const wcSubs = /* @__PURE__ */ new Map();
|
|
3416
|
+
const onWcCreated = (_event, wc) => {
|
|
3417
|
+
const sub = new DisposableRegistry();
|
|
3418
|
+
wcSubs.set(wc, sub);
|
|
3419
|
+
const onFinishLoad = () => {
|
|
3420
|
+
if (isSimulatorWebview(wc)) void attachToSim(wc);
|
|
3421
|
+
};
|
|
3422
|
+
wc.on("did-finish-load", onFinishLoad);
|
|
3423
|
+
sub.add(() => safeOff(wc, "did-finish-load", onFinishLoad));
|
|
3424
|
+
wc.once("destroyed", () => {
|
|
3425
|
+
void sub.disposeAll().catch(() => {
|
|
3426
|
+
});
|
|
3427
|
+
wcSubs.delete(wc);
|
|
3428
|
+
});
|
|
3429
|
+
};
|
|
3430
|
+
app10.on("web-contents-created", onWcCreated);
|
|
3431
|
+
registry.add(() => {
|
|
3432
|
+
app10.removeListener("web-contents-created", onWcCreated);
|
|
3433
|
+
});
|
|
3434
|
+
registry.add(async () => {
|
|
3435
|
+
const subs = Array.from(wcSubs.values());
|
|
3436
|
+
wcSubs.clear();
|
|
3437
|
+
for (const sub of subs) {
|
|
3438
|
+
await sub.disposeAll().catch(() => {
|
|
3439
|
+
});
|
|
3440
|
+
}
|
|
3441
|
+
});
|
|
3442
|
+
const ipc = new IpcRegistry(options.senderPolicy);
|
|
3443
|
+
ipc.handle(SimulatorStorageChannel.GetSnapshot, () => getSnapshot());
|
|
3444
|
+
ipc.handle(SimulatorStorageChannel.GetActivePrefix, () => activePrefix() ?? "");
|
|
3445
|
+
ipc.handle(
|
|
3446
|
+
SimulatorStorageChannel.Set,
|
|
3447
|
+
(_event, payload) => setItem(String(payload?.key ?? ""), String(payload?.value ?? ""))
|
|
3448
|
+
);
|
|
3449
|
+
ipc.handle(
|
|
3450
|
+
SimulatorStorageChannel.Remove,
|
|
3451
|
+
(_event, payload) => removeItem(String(payload?.key ?? ""))
|
|
3452
|
+
);
|
|
3453
|
+
ipc.handle(SimulatorStorageChannel.Clear, () => clearScoped());
|
|
3454
|
+
ipc.handle(SimulatorStorageChannel.ClearAll, () => clearAll());
|
|
3455
|
+
ipc.handle(SimulatorElementChannel.Inspect, (_event, sid) => inspectElement(sid));
|
|
3456
|
+
ipc.handle(SimulatorElementChannel.Clear, () => clearElementInspection());
|
|
3457
|
+
registry.add(ipc);
|
|
3458
|
+
registry.add(() => detachFromSim());
|
|
3459
|
+
return registry;
|
|
2392
3460
|
}
|
|
2393
3461
|
function findSimulatorWebContents() {
|
|
2394
3462
|
for (const wc of wcStatic.getAllWebContents()) {
|
|
@@ -2420,30 +3488,9 @@ async function clearElementInspection() {
|
|
|
2420
3488
|
} catch {
|
|
2421
3489
|
}
|
|
2422
3490
|
}
|
|
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
3491
|
|
|
2445
3492
|
// src/main/services/update/update-manager.ts
|
|
2446
|
-
import { app as
|
|
3493
|
+
import { app as app11, shell as shell3 } from "electron";
|
|
2447
3494
|
var UpdateManager = class {
|
|
2448
3495
|
checker;
|
|
2449
3496
|
mainWindow;
|
|
@@ -2452,23 +3499,17 @@ var UpdateManager = class {
|
|
|
2452
3499
|
downloadedPath = null;
|
|
2453
3500
|
timer = null;
|
|
2454
3501
|
initialTimer = null;
|
|
3502
|
+
ipc;
|
|
2455
3503
|
constructor(opts) {
|
|
2456
3504
|
this.checker = opts.checker;
|
|
2457
3505
|
this.mainWindow = opts.mainWindow;
|
|
2458
|
-
this.getCurrentVersion = opts.getCurrentVersion ?? (() =>
|
|
3506
|
+
this.getCurrentVersion = opts.getCurrentVersion ?? (() => app11.getVersion());
|
|
3507
|
+
this.ipc = new IpcRegistry(opts.senderPolicy);
|
|
2459
3508
|
this.registerIpc();
|
|
2460
3509
|
this.startPeriodicCheck(opts.checkInterval ?? 60 * 60 * 1e3, opts.initialDelay ?? 5e3);
|
|
2461
3510
|
}
|
|
2462
3511
|
registerIpc() {
|
|
2463
|
-
|
|
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
|
-
});
|
|
3512
|
+
this.ipc.handle(UpdateChannel.Check, async () => this.check()).handle(UpdateChannel.Download, async () => this.download()).handle(UpdateChannel.Install, async () => this.install());
|
|
2472
3513
|
}
|
|
2473
3514
|
async check() {
|
|
2474
3515
|
try {
|
|
@@ -2491,7 +3532,7 @@ var UpdateManager = class {
|
|
|
2491
3532
|
}
|
|
2492
3533
|
try {
|
|
2493
3534
|
const filePath = await this.checker.downloadUpdate(this.latestUpdate, (percent) => {
|
|
2494
|
-
this.mainWindow.webContents.send(
|
|
3535
|
+
this.mainWindow.webContents.send(UpdateChannel.DownloadProgress, { percent });
|
|
2495
3536
|
});
|
|
2496
3537
|
this.downloadedPath = filePath;
|
|
2497
3538
|
return { success: true, filePath };
|
|
@@ -2502,10 +3543,16 @@ var UpdateManager = class {
|
|
|
2502
3543
|
}
|
|
2503
3544
|
install() {
|
|
2504
3545
|
if (this.downloadedPath) {
|
|
2505
|
-
|
|
2506
|
-
|
|
3546
|
+
shell3.openPath(this.downloadedPath);
|
|
3547
|
+
app11.quit();
|
|
2507
3548
|
}
|
|
2508
3549
|
}
|
|
3550
|
+
/**
|
|
3551
|
+
* Tear down timers + IPC handlers. Returns a Promise so callers that need
|
|
3552
|
+
* a settled cleanup (e.g. workbench registry shutdown, the unit-test
|
|
3553
|
+
* lifecycle suite) can await it; calling without await is also safe since
|
|
3554
|
+
* the timer clears + handler removals are scheduled synchronously.
|
|
3555
|
+
*/
|
|
2509
3556
|
dispose() {
|
|
2510
3557
|
if (this.timer) {
|
|
2511
3558
|
clearInterval(this.timer);
|
|
@@ -2515,9 +3562,7 @@ var UpdateManager = class {
|
|
|
2515
3562
|
clearTimeout(this.initialTimer);
|
|
2516
3563
|
this.initialTimer = null;
|
|
2517
3564
|
}
|
|
2518
|
-
|
|
2519
|
-
ipcMain11.removeHandler("updates:download");
|
|
2520
|
-
ipcMain11.removeHandler("updates:install");
|
|
3565
|
+
return this.ipc.dispose();
|
|
2521
3566
|
}
|
|
2522
3567
|
startPeriodicCheck(interval, initialDelay) {
|
|
2523
3568
|
this.initialTimer = setTimeout(() => void this.checkAndNotify(), initialDelay);
|
|
@@ -2526,13 +3571,13 @@ var UpdateManager = class {
|
|
|
2526
3571
|
async checkAndNotify() {
|
|
2527
3572
|
const result = await this.check();
|
|
2528
3573
|
if (result.hasUpdate && result.info) {
|
|
2529
|
-
this.mainWindow.webContents.send(
|
|
3574
|
+
this.mainWindow.webContents.send(UpdateChannel.Available, result.info);
|
|
2530
3575
|
}
|
|
2531
3576
|
}
|
|
2532
3577
|
};
|
|
2533
3578
|
|
|
2534
3579
|
// src/main/services/update/github-release-checker.ts
|
|
2535
|
-
import { app as
|
|
3580
|
+
import { app as app12 } from "electron";
|
|
2536
3581
|
|
|
2537
3582
|
// src/main/app/app.ts
|
|
2538
3583
|
var DEFAULT_MODULES = {
|
|
@@ -2542,16 +3587,12 @@ var DEFAULT_MODULES = {
|
|
|
2542
3587
|
popover: true,
|
|
2543
3588
|
settings: true
|
|
2544
3589
|
};
|
|
2545
|
-
var
|
|
2546
|
-
projects:
|
|
2547
|
-
session:
|
|
2548
|
-
simulator:
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
registerToolbarIpc(ctx);
|
|
2552
|
-
},
|
|
2553
|
-
popover: registerPopoverIpc,
|
|
2554
|
-
settings: registerSettingsIpc
|
|
3590
|
+
var BUILTIN_MODULES = {
|
|
3591
|
+
projects: projectsModule,
|
|
3592
|
+
session: sessionModule,
|
|
3593
|
+
simulator: simulatorModule,
|
|
3594
|
+
popover: popoverModule,
|
|
3595
|
+
settings: settingsModule
|
|
2555
3596
|
};
|
|
2556
3597
|
function parseAutoArgs() {
|
|
2557
3598
|
const argv = process.argv;
|
|
@@ -2576,18 +3617,25 @@ function resolveModules(config) {
|
|
|
2576
3617
|
...config.modules
|
|
2577
3618
|
};
|
|
2578
3619
|
}
|
|
2579
|
-
async function disposeContext(ctx
|
|
2580
|
-
updateManager?.dispose();
|
|
3620
|
+
async function disposeContext(ctx) {
|
|
2581
3621
|
await ctx.workspace.closeProject();
|
|
3622
|
+
await ctx.registry.dispose().catch((err) => {
|
|
3623
|
+
console.warn("[workbench] dispose registry encountered errors:", err);
|
|
3624
|
+
});
|
|
2582
3625
|
}
|
|
2583
3626
|
function createConfiguredMainWindow(config, rendererDir2) {
|
|
2584
3627
|
const mainWindow = createMainWindow({
|
|
2585
3628
|
title: config.appName ?? "Dimina DevTools",
|
|
2586
|
-
indexHtml:
|
|
3629
|
+
indexHtml: path12.join(rendererDir2, "entries/main/index.html"),
|
|
2587
3630
|
width: config.window?.width,
|
|
2588
3631
|
height: config.window?.height,
|
|
2589
3632
|
minWidth: config.window?.minWidth,
|
|
2590
|
-
minHeight: config.window?.minHeight
|
|
3633
|
+
minHeight: config.window?.minHeight,
|
|
3634
|
+
// P1: pass the same preload path the workbench context stores on
|
|
3635
|
+
// ctx.preloadPath so hosts that ship a custom simulator preload are the
|
|
3636
|
+
// ones registered on the persist:simulator session — otherwise the
|
|
3637
|
+
// backstop wouldn't be the host's script.
|
|
3638
|
+
simulatorPreloadPath: config.preloadPath ?? defaultPreloadPath
|
|
2591
3639
|
});
|
|
2592
3640
|
if (config.icon) {
|
|
2593
3641
|
const icon = nativeImage.createFromPath(config.icon);
|
|
@@ -2605,13 +3653,21 @@ function createContext(config, mainWindow, rendererDir2) {
|
|
|
2605
3653
|
appName: config.appName,
|
|
2606
3654
|
apiNamespaces: config.apiNamespaces,
|
|
2607
3655
|
brandingProvider: config.brandingProvider,
|
|
2608
|
-
toolbarActions: config.toolbarActions
|
|
3656
|
+
toolbarActions: config.toolbarActions,
|
|
3657
|
+
// The host-supplied ProjectsProvider / template types in `shared/types`
|
|
3658
|
+
// are structurally compatible with the main-process equivalents —
|
|
3659
|
+
// these casts are safe; we re-narrow at the workspace-service /
|
|
3660
|
+
// create-project-service boundary.
|
|
3661
|
+
projectsProvider: config.projectsProvider,
|
|
3662
|
+
projectTemplates: config.projectTemplates,
|
|
3663
|
+
builtinTemplates: config.builtinTemplates,
|
|
3664
|
+
customCreateProjectDialog: config.customCreateProjectDialog
|
|
2609
3665
|
});
|
|
2610
3666
|
}
|
|
2611
3667
|
function registerBuiltinModules(config, context) {
|
|
2612
3668
|
const modules = resolveModules(config);
|
|
2613
3669
|
Object.keys(modules).forEach((moduleId) => {
|
|
2614
|
-
if (modules[moduleId])
|
|
3670
|
+
if (modules[moduleId]) context.registry.add(BUILTIN_MODULES[moduleId].setup(context));
|
|
2615
3671
|
});
|
|
2616
3672
|
}
|
|
2617
3673
|
function installMenu(config, mainWindow, context) {
|
|
@@ -2626,19 +3682,20 @@ async function setupAutomation(instance) {
|
|
|
2626
3682
|
if (autoArgs.auto) {
|
|
2627
3683
|
const server = await startAutomationServer(instance.context, autoArgs.autoPort);
|
|
2628
3684
|
instance.automationServer = server;
|
|
3685
|
+
instance.context.registry.add(() => server.close());
|
|
2629
3686
|
console.log(`[automation] listening on ws://127.0.0.1:${server.port}`);
|
|
2630
3687
|
}
|
|
2631
3688
|
}
|
|
2632
3689
|
function setupMcp() {
|
|
2633
3690
|
const settings = loadWorkbenchSettings();
|
|
2634
|
-
if (!settings.mcp.enabled) return;
|
|
2635
|
-
const cdpPortSwitch =
|
|
3691
|
+
if (!settings.mcp.enabled) return null;
|
|
3692
|
+
const cdpPortSwitch = app13.commandLine.getSwitchValue("remote-debugging-port");
|
|
2636
3693
|
const cdpPort2 = cdpPortSwitch ? parseInt(cdpPortSwitch, 10) : settings.cdp.port;
|
|
2637
|
-
startMcpServer(cdpPort2, settings.mcp.port);
|
|
3694
|
+
return startMcpServer(cdpPort2, settings.mcp.port);
|
|
2638
3695
|
}
|
|
2639
3696
|
function wireAppWindowEvents(config, instance) {
|
|
2640
3697
|
const { mainWindow, context } = instance;
|
|
2641
|
-
wireMainWindowEvents(mainWindow, {
|
|
3698
|
+
return wireMainWindowEvents(mainWindow, {
|
|
2642
3699
|
context,
|
|
2643
3700
|
onResize: () => context.views.repositionAll(),
|
|
2644
3701
|
onClose: async (e) => {
|
|
@@ -2647,23 +3704,32 @@ function wireAppWindowEvents(config, instance) {
|
|
|
2647
3704
|
if (config.onBeforeClose) {
|
|
2648
3705
|
await config.onBeforeClose(instance);
|
|
2649
3706
|
}
|
|
2650
|
-
await
|
|
3707
|
+
await context.workspace.closeProject();
|
|
2651
3708
|
context.notify.windowNavigateBack();
|
|
2652
3709
|
}
|
|
2653
3710
|
});
|
|
2654
3711
|
}
|
|
2655
3712
|
function enableDevRendererAutoReload(rendererDir2) {
|
|
2656
|
-
if (
|
|
2657
|
-
|
|
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);
|
|
3713
|
+
if (app13.isPackaged) {
|
|
3714
|
+
return toDisposable(() => {
|
|
2665
3715
|
});
|
|
2666
3716
|
}
|
|
3717
|
+
let reloadTimer = null;
|
|
3718
|
+
const watcher = fs7.watch(rendererDir2, { recursive: true }, () => {
|
|
3719
|
+
if (reloadTimer) clearTimeout(reloadTimer);
|
|
3720
|
+
reloadTimer = setTimeout(() => {
|
|
3721
|
+
for (const win of BrowserWindow4.getAllWindows()) {
|
|
3722
|
+
win.webContents.reload();
|
|
3723
|
+
}
|
|
3724
|
+
}, 300);
|
|
3725
|
+
});
|
|
3726
|
+
return toDisposable(() => {
|
|
3727
|
+
if (reloadTimer) {
|
|
3728
|
+
clearTimeout(reloadTimer);
|
|
3729
|
+
reloadTimer = null;
|
|
3730
|
+
}
|
|
3731
|
+
watcher.close();
|
|
3732
|
+
});
|
|
2667
3733
|
}
|
|
2668
3734
|
function createWorkbenchApp(config = {}) {
|
|
2669
3735
|
let setupPromise = null;
|
|
@@ -2671,18 +3737,19 @@ function createWorkbenchApp(config = {}) {
|
|
|
2671
3737
|
setupCdpPort();
|
|
2672
3738
|
async function setup() {
|
|
2673
3739
|
if (setupPromise) return setupPromise;
|
|
2674
|
-
setupPromise =
|
|
3740
|
+
setupPromise = app13.whenReady().then(async () => {
|
|
2675
3741
|
applyTheme(loadWorkbenchSettings().theme);
|
|
2676
3742
|
const rendererDir2 = config.rendererDir ?? rendererDir;
|
|
2677
3743
|
const mainWindow = createConfiguredMainWindow(config, rendererDir2);
|
|
2678
3744
|
const context = createContext(config, mainWindow, rendererDir2);
|
|
2679
|
-
registerAppIpc(context);
|
|
3745
|
+
context.registry.add(registerAppIpc(context));
|
|
3746
|
+
context.registry.add(installThemeBackgroundSync());
|
|
2680
3747
|
registerBuiltinModules(config, context);
|
|
2681
3748
|
installMenu(config, mainWindow, context);
|
|
2682
3749
|
const instance = {
|
|
2683
3750
|
mainWindow,
|
|
2684
3751
|
context,
|
|
2685
|
-
dispose: () => disposeContext(context
|
|
3752
|
+
dispose: () => disposeContext(context)
|
|
2686
3753
|
};
|
|
2687
3754
|
if (config.onSetup) {
|
|
2688
3755
|
await config.onSetup(instance);
|
|
@@ -2695,22 +3762,37 @@ function createWorkbenchApp(config = {}) {
|
|
|
2695
3762
|
initialDelay: config.updateOptions?.initialDelay,
|
|
2696
3763
|
getCurrentVersion: config.updateOptions?.getCurrentVersion
|
|
2697
3764
|
});
|
|
3765
|
+
context.registry.add(() => instance.updateManager.dispose());
|
|
2698
3766
|
}
|
|
2699
3767
|
await setupAutomation(instance);
|
|
2700
|
-
setupMcp();
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
3768
|
+
const mcp = setupMcp();
|
|
3769
|
+
if (mcp) context.registry.add(mcp);
|
|
3770
|
+
context.registry.add(setupSimulatorStorage(mainWindow.webContents, {
|
|
3771
|
+
senderPolicy: context.senderPolicy,
|
|
3772
|
+
// Per-project filter for the simulator-storage panel: the simulator
|
|
3773
|
+
// uses a fixed `persist:simulator` partition + a fixed simulator.html
|
|
3774
|
+
// origin, so localStorage is shared across every project that has
|
|
3775
|
+
// ever opened. The dimina runtime isolates writes with `${appId}_`
|
|
3776
|
+
// prefixes; this callback lets the storage panel filter the CDP
|
|
3777
|
+
// snapshot/event stream to the active appId.
|
|
3778
|
+
getActiveAppId: () => {
|
|
3779
|
+
const session2 = context.workspace.getSession();
|
|
3780
|
+
const appInfo = session2?.appInfo;
|
|
3781
|
+
return appInfo?.appId ?? null;
|
|
3782
|
+
}
|
|
3783
|
+
}));
|
|
3784
|
+
context.registry.add(wireAppWindowEvents(config, instance));
|
|
3785
|
+
context.registry.add(enableDevRendererAutoReload(rendererDir2));
|
|
2704
3786
|
return instance;
|
|
2705
3787
|
});
|
|
2706
3788
|
return setupPromise;
|
|
2707
3789
|
}
|
|
2708
3790
|
function start() {
|
|
2709
|
-
void setup();
|
|
2710
3791
|
if (!appEventsRegistered) {
|
|
2711
3792
|
appEventsRegistered = true;
|
|
2712
3793
|
registerAppLifecycle();
|
|
2713
3794
|
}
|
|
3795
|
+
return setup().then(() => void 0);
|
|
2714
3796
|
}
|
|
2715
3797
|
return {
|
|
2716
3798
|
setup,
|
|
@@ -2719,10 +3801,10 @@ function createWorkbenchApp(config = {}) {
|
|
|
2719
3801
|
}
|
|
2720
3802
|
|
|
2721
3803
|
// src/main/windows/settings-window/create.ts
|
|
2722
|
-
import { BrowserWindow as
|
|
2723
|
-
import
|
|
3804
|
+
import { BrowserWindow as BrowserWindow5 } from "electron";
|
|
3805
|
+
import path13 from "path";
|
|
2724
3806
|
async function createSettingsWindow(parent, rendererDir2) {
|
|
2725
|
-
const win = new
|
|
3807
|
+
const win = new BrowserWindow5({
|
|
2726
3808
|
width: 420,
|
|
2727
3809
|
height: 560,
|
|
2728
3810
|
minWidth: 380,
|
|
@@ -2730,9 +3812,15 @@ async function createSettingsWindow(parent, rendererDir2) {
|
|
|
2730
3812
|
parent: parent ?? void 0,
|
|
2731
3813
|
title: "\u5F00\u53D1\u5DE5\u5177\u8BBE\u7F6E",
|
|
2732
3814
|
backgroundColor: themeBg(),
|
|
2733
|
-
webPreferences: {
|
|
3815
|
+
webPreferences: {
|
|
3816
|
+
nodeIntegration: false,
|
|
3817
|
+
contextIsolation: true,
|
|
3818
|
+
sandbox: false,
|
|
3819
|
+
preload: mainPreloadPath
|
|
3820
|
+
}
|
|
2734
3821
|
});
|
|
2735
|
-
|
|
3822
|
+
applyNavigationHardening(win.webContents, rendererDir2);
|
|
3823
|
+
await win.loadFile(path13.join(rendererDir2, "entries/workbench-settings/index.html"));
|
|
2736
3824
|
return win;
|
|
2737
3825
|
}
|
|
2738
3826
|
|
|
@@ -2745,18 +3833,20 @@ function wireSettingsWindowEvents(win, onClosed) {
|
|
|
2745
3833
|
|
|
2746
3834
|
// src/main/app/launch.ts
|
|
2747
3835
|
function launch(config = {}) {
|
|
2748
|
-
createWorkbenchApp(config).start();
|
|
3836
|
+
return createWorkbenchApp(config).start();
|
|
2749
3837
|
}
|
|
2750
3838
|
async function openSettingsWindow(ctx) {
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
3839
|
+
let win = ctx.windows.settingsWindow;
|
|
3840
|
+
if (!win || win.isDestroyed()) {
|
|
3841
|
+
win = await createSettingsWindow(ctx.windows.mainWindow, ctx.rendererDir);
|
|
3842
|
+
ctx.windows.setSettingsWindow(win);
|
|
3843
|
+
wireSettingsWindowEvents(win, () => {
|
|
3844
|
+
ctx.windows.setSettingsWindow(null);
|
|
2755
3845
|
});
|
|
2756
3846
|
}
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
ctx.notify.workbenchSettingsInit(
|
|
3847
|
+
win.show();
|
|
3848
|
+
win.focus();
|
|
3849
|
+
ctx.notify.workbenchSettingsInit(win, {
|
|
2760
3850
|
settings: loadWorkbenchSettings()
|
|
2761
3851
|
});
|
|
2762
3852
|
}
|