@dimina-kit/devtools 0.3.1-dev.20260513123738 → 0.3.1-dev.20260515105504
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 +103 -41
- 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 +71 -36
- package/dist/main/app/launch.d.ts +2 -2
- package/dist/main/app/launch.js +6 -3
- package/dist/main/index.bundle.js +1074 -289
- 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 +14 -9
- package/dist/main/ipc/popover.d.ts +4 -1
- package/dist/main/ipc/popover.js +15 -5
- package/dist/main/ipc/projects.d.ts +4 -1
- package/dist/main/ipc/projects.js +42 -5
- package/dist/main/ipc/session.d.ts +4 -1
- package/dist/main/ipc/session.js +26 -6
- package/dist/main/ipc/settings.d.ts +4 -1
- package/dist/main/ipc/settings.js +30 -17
- 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 +29 -15
- package/dist/main/ipc/toolbar.d.ts +2 -1
- package/dist/main/ipc/toolbar.js +3 -2
- package/dist/main/services/automation/index.js +88 -13
- package/dist/main/services/mcp/server.d.ts +2 -1
- package/dist/main/services/mcp/server.js +5 -0
- 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 +2 -0
- package/dist/main/services/projects/project-repository.d.ts +1 -0
- package/dist/main/services/projects/project-repository.js +3 -0
- package/dist/main/services/settings/index.d.ts +4 -0
- package/dist/main/services/settings/index.js +12 -1
- 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 +365 -98
- 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 +4 -0
- package/dist/main/services/views/view-manager.js +33 -2
- 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 +15 -0
- package/dist/main/services/workbench-context.js +6 -0
- package/dist/main/services/workspace/workspace-service.d.ts +1 -0
- package/dist/main/services/workspace/workspace-service.js +7 -3
- 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 +23 -0
- package/dist/main/utils/sender-policy.js +42 -0
- package/dist/main/windows/main-window/create.d.ts +6 -0
- package/dist/main/windows/main-window/create.js +65 -9
- 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 +17 -0
- package/dist/preload/index.js +17 -0
- package/dist/preload/instrumentation/app-data.js +178 -19
- package/dist/preload/runtime/custom-apis.d.ts +6 -0
- package/dist/preload/runtime/custom-apis.js +18 -0
- package/dist/preload/runtime/host.d.ts +1 -0
- package/dist/preload/runtime/host.js +13 -0
- package/dist/preload/windows/main.cjs +47 -0
- package/dist/preload/windows/main.cjs.map +7 -0
- package/dist/preload/windows/simulator.js +174 -18
- package/dist/renderer/assets/index-DW_0thr-.js +46 -0
- package/dist/renderer/assets/{input-DxfYnzX_.js → input-C5M5Wxn6.js} +2 -2
- package/dist/renderer/assets/{ipc-transport-ClIvDcYY.js → ipc-transport-CUsl-fS2.js} +2 -2
- package/dist/renderer/assets/ipc-transport-CltRuvhi.css +1 -0
- package/dist/renderer/assets/{popover-CtXcBJtM.js → popover-BSBPVO0o.js} +2 -2
- package/dist/renderer/assets/{select-DMfp49f1.js → select-B1tF3UAc.js} +2 -2
- package/dist/renderer/assets/{settings-DteGraEU.js → settings-7w54H6p-.js} +2 -2
- package/dist/renderer/assets/settings-api-BjJalghB.js +2 -0
- package/dist/renderer/assets/workbenchSettings-q2_-nc8t.js +8 -0
- package/dist/renderer/entries/main/index.html +12 -5
- package/dist/renderer/entries/popover/index.html +7 -5
- package/dist/renderer/entries/settings/index.html +7 -5
- package/dist/renderer/entries/workbench-settings/index.html +6 -4
- package/dist/shared/ipc-channels.d.ts +22 -0
- package/dist/shared/ipc-channels.js +23 -0
- package/dist/shared/ipc-schemas.d.ts +113 -0
- package/dist/shared/ipc-schemas.js +128 -0
- package/dist/shared/types.d.ts +2 -0
- package/dist/simulator/assets/simulator-DL6kOCq-.js +9 -0
- package/dist/simulator/simulator.html +1 -1
- package/package.json +11 -27
- 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-EiWxu_hG.css +0 -1
- 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
|
@@ -14,6 +14,9 @@ var DEFAULTS = {
|
|
|
14
14
|
enabled: false,
|
|
15
15
|
port: 7789
|
|
16
16
|
},
|
|
17
|
+
compile: {
|
|
18
|
+
watch: true
|
|
19
|
+
},
|
|
17
20
|
theme: "system"
|
|
18
21
|
};
|
|
19
22
|
function getSettingsFile() {
|
|
@@ -32,10 +35,18 @@ function loadWorkbenchSettings() {
|
|
|
32
35
|
enabled: data.mcp?.enabled ?? DEFAULTS.mcp.enabled,
|
|
33
36
|
port: data.mcp?.port ?? DEFAULTS.mcp.port
|
|
34
37
|
},
|
|
38
|
+
compile: {
|
|
39
|
+
watch: data.compile?.watch ?? DEFAULTS.compile.watch
|
|
40
|
+
},
|
|
35
41
|
theme
|
|
36
42
|
};
|
|
37
43
|
} catch {
|
|
38
|
-
return {
|
|
44
|
+
return {
|
|
45
|
+
...DEFAULTS,
|
|
46
|
+
cdp: { ...DEFAULTS.cdp },
|
|
47
|
+
mcp: { ...DEFAULTS.mcp },
|
|
48
|
+
compile: { ...DEFAULTS.compile }
|
|
49
|
+
};
|
|
39
50
|
}
|
|
40
51
|
}
|
|
41
52
|
function saveWorkbenchSettings(settings) {
|
|
@@ -64,7 +75,7 @@ function setupCdpPort() {
|
|
|
64
75
|
// src/main/app/app.ts
|
|
65
76
|
import { app as app11, BrowserWindow as BrowserWindow3, nativeImage } from "electron";
|
|
66
77
|
import fs5 from "fs";
|
|
67
|
-
import
|
|
78
|
+
import path8 from "path";
|
|
68
79
|
|
|
69
80
|
// src/main/utils/paths.ts
|
|
70
81
|
import fs2 from "fs";
|
|
@@ -87,6 +98,7 @@ function resolveDevtoolsPackageRoot() {
|
|
|
87
98
|
var devtoolsPackageRoot = resolveDevtoolsPackageRoot();
|
|
88
99
|
var rendererDir = path2.join(devtoolsPackageRoot, "dist/renderer");
|
|
89
100
|
var defaultPreloadPath = path2.join(devtoolsPackageRoot, "dist/preload/windows/simulator.js");
|
|
101
|
+
var mainPreloadPath = path2.join(devtoolsPackageRoot, "dist/preload/windows/main.cjs");
|
|
90
102
|
var simulatorDir = path2.join(devtoolsPackageRoot, "dist/simulator");
|
|
91
103
|
|
|
92
104
|
// src/main/app/lifecycle.ts
|
|
@@ -102,7 +114,8 @@ function registerAppLifecycle() {
|
|
|
102
114
|
}
|
|
103
115
|
|
|
104
116
|
// src/main/windows/main-window/create.ts
|
|
105
|
-
import { app as app4, BrowserWindow, View,
|
|
117
|
+
import { app as app4, BrowserWindow, View, session, shell as shell2 } from "electron";
|
|
118
|
+
import path4 from "path";
|
|
106
119
|
|
|
107
120
|
// src/main/services/simulator/referer.ts
|
|
108
121
|
var DEFAULT_VERSION = "develop";
|
|
@@ -129,12 +142,51 @@ function themeBg() {
|
|
|
129
142
|
return nativeTheme2.shouldUseDarkColors ? "#1a1a1a" : "#fafafa";
|
|
130
143
|
}
|
|
131
144
|
|
|
145
|
+
// src/main/windows/navigation-hardening.ts
|
|
146
|
+
import { shell } from "electron";
|
|
147
|
+
import path3 from "path";
|
|
148
|
+
import { pathToFileURL } from "url";
|
|
149
|
+
function handleWindowOpenExternal(url) {
|
|
150
|
+
try {
|
|
151
|
+
const u = new URL(url);
|
|
152
|
+
if (u.protocol === "http:" || u.protocol === "https:") {
|
|
153
|
+
void shell.openExternal(url);
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
return { action: "deny" };
|
|
158
|
+
}
|
|
159
|
+
function applyNavigationHardening(wc, rendererDir2) {
|
|
160
|
+
const rendererPrefix = pathToFileURL(rendererDir2 + path3.sep).href;
|
|
161
|
+
wc.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
|
|
162
|
+
wc.on("will-navigate", (event, url) => {
|
|
163
|
+
let parsed;
|
|
164
|
+
try {
|
|
165
|
+
parsed = new URL(url);
|
|
166
|
+
} catch {
|
|
167
|
+
event.preventDefault();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (parsed.protocol === "file:" && parsed.href.startsWith(rendererPrefix)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
event.preventDefault();
|
|
174
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
175
|
+
void shell.openExternal(url);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
132
180
|
// src/main/windows/main-window/create.ts
|
|
133
181
|
var simulatorSessionConfigured = false;
|
|
134
|
-
function configureSimulatorSession() {
|
|
182
|
+
function configureSimulatorSession(simulatorPreloadPath) {
|
|
135
183
|
if (simulatorSessionConfigured) return;
|
|
136
184
|
simulatorSessionConfigured = true;
|
|
137
185
|
const simulatorSession = session.fromPartition("persist:simulator");
|
|
186
|
+
simulatorSession.registerPreloadScript({
|
|
187
|
+
type: "frame",
|
|
188
|
+
filePath: simulatorPreloadPath
|
|
189
|
+
});
|
|
138
190
|
simulatorSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
|
139
191
|
const forcedReferer = getSimulatorServicewechatReferer();
|
|
140
192
|
if (forcedReferer) {
|
|
@@ -153,7 +205,7 @@ function configureSimulatorSession() {
|
|
|
153
205
|
});
|
|
154
206
|
}
|
|
155
207
|
function createMainWindow(opts) {
|
|
156
|
-
configureSimulatorSession();
|
|
208
|
+
configureSimulatorSession(opts.simulatorPreloadPath);
|
|
157
209
|
const mainWindow = new BrowserWindow({
|
|
158
210
|
width: opts.width ?? 1280,
|
|
159
211
|
height: opts.height ?? 980,
|
|
@@ -163,8 +215,13 @@ function createMainWindow(opts) {
|
|
|
163
215
|
show: false,
|
|
164
216
|
backgroundColor: themeBg(),
|
|
165
217
|
webPreferences: {
|
|
166
|
-
nodeIntegration:
|
|
167
|
-
contextIsolation:
|
|
218
|
+
nodeIntegration: false,
|
|
219
|
+
contextIsolation: true,
|
|
220
|
+
// `sandbox: false` is required so the preload bundle can `require('electron')`
|
|
221
|
+
// to access contextBridge/ipcRenderer. The renderer itself runs isolated.
|
|
222
|
+
sandbox: false,
|
|
223
|
+
preload: mainPreloadPath,
|
|
224
|
+
// The simulator <webview> still relies on the webview tag; keep it enabled.
|
|
168
225
|
webviewTag: true
|
|
169
226
|
}
|
|
170
227
|
});
|
|
@@ -178,22 +235,102 @@ function createMainWindow(opts) {
|
|
|
178
235
|
}
|
|
179
236
|
}
|
|
180
237
|
});
|
|
181
|
-
|
|
238
|
+
const rendererDir2 = path4.dirname(opts.indexHtml);
|
|
239
|
+
applyNavigationHardening(mainWindow.webContents, rendererDir2);
|
|
240
|
+
mainWindow.webContents.on("will-attach-webview", (event, webPreferences, params) => {
|
|
241
|
+
;
|
|
242
|
+
webPreferences.partition = "persist:simulator";
|
|
243
|
+
params.partition = "persist:simulator";
|
|
182
244
|
webPreferences.contextIsolation = false;
|
|
245
|
+
void event;
|
|
246
|
+
});
|
|
247
|
+
mainWindow.webContents.on("did-attach-webview", (_event, webviewWebContents) => {
|
|
248
|
+
webviewWebContents.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
|
|
249
|
+
webviewWebContents.on("will-navigate", (e, url) => {
|
|
250
|
+
try {
|
|
251
|
+
const u = new URL(url);
|
|
252
|
+
if (u.protocol === "about:") return;
|
|
253
|
+
if ((u.protocol === "http:" || u.protocol === "https:") && (u.hostname === "localhost" || u.hostname === "127.0.0.1")) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
if (u.protocol === "http:" || u.protocol === "https:") {
|
|
258
|
+
void shell2.openExternal(url);
|
|
259
|
+
}
|
|
260
|
+
} catch {
|
|
261
|
+
e.preventDefault();
|
|
262
|
+
}
|
|
263
|
+
});
|
|
183
264
|
});
|
|
184
265
|
mainWindow.loadFile(opts.indexHtml);
|
|
185
266
|
const container = new View();
|
|
186
267
|
const mainWebView = mainWindow.contentView;
|
|
187
268
|
container.addChildView(mainWebView);
|
|
188
269
|
mainWindow.contentView = container;
|
|
189
|
-
globalShortcut2.register("CommandOrControl+Shift+I", () => {
|
|
190
|
-
mainWindow.webContents.openDevTools({ mode: "detach" });
|
|
191
|
-
});
|
|
192
270
|
return mainWindow;
|
|
193
271
|
}
|
|
194
272
|
|
|
273
|
+
// src/main/windows/main-window/events.ts
|
|
274
|
+
import { globalShortcut as globalShortcut2 } from "electron";
|
|
275
|
+
|
|
276
|
+
// src/main/utils/disposable.ts
|
|
277
|
+
function toDisposable(fn) {
|
|
278
|
+
let done = false;
|
|
279
|
+
return {
|
|
280
|
+
dispose() {
|
|
281
|
+
if (done) return;
|
|
282
|
+
done = true;
|
|
283
|
+
return fn();
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
var DisposableRegistry = class {
|
|
288
|
+
entries = [];
|
|
289
|
+
_disposed = false;
|
|
290
|
+
add(d) {
|
|
291
|
+
if (this._disposed) {
|
|
292
|
+
throw new Error("cannot add to disposed registry");
|
|
293
|
+
}
|
|
294
|
+
const fn = typeof d === "function" ? d : () => d.dispose();
|
|
295
|
+
const entry = { fn, released: false };
|
|
296
|
+
this.entries.push(entry);
|
|
297
|
+
return {
|
|
298
|
+
dispose: () => {
|
|
299
|
+
if (entry.released) return;
|
|
300
|
+
entry.released = true;
|
|
301
|
+
const i = this.entries.indexOf(entry);
|
|
302
|
+
if (i >= 0) this.entries.splice(i, 1);
|
|
303
|
+
return fn();
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
async disposeAll() {
|
|
308
|
+
if (this._disposed) return;
|
|
309
|
+
this._disposed = true;
|
|
310
|
+
const items = this.entries.slice().reverse();
|
|
311
|
+
this.entries = [];
|
|
312
|
+
const errors = [];
|
|
313
|
+
for (const entry of items) {
|
|
314
|
+
if (entry.released) continue;
|
|
315
|
+
entry.released = true;
|
|
316
|
+
try {
|
|
317
|
+
await entry.fn();
|
|
318
|
+
} catch (e) {
|
|
319
|
+
errors.push(e);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (errors.length > 0) {
|
|
323
|
+
throw new AggregateError(errors, "DisposableRegistry encountered errors during disposeAll");
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
dispose() {
|
|
327
|
+
return this.disposeAll();
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
195
331
|
// src/main/windows/main-window/events.ts
|
|
196
332
|
function wireMainWindowEvents(win, state = {}) {
|
|
333
|
+
const registry = new DisposableRegistry();
|
|
197
334
|
const mainWebView = win.contentView.children[0];
|
|
198
335
|
const resizeMainWebView = () => {
|
|
199
336
|
if (mainWebView) {
|
|
@@ -204,22 +341,53 @@ function wireMainWindowEvents(win, state = {}) {
|
|
|
204
341
|
};
|
|
205
342
|
resizeMainWebView();
|
|
206
343
|
win.on("resize", resizeMainWebView);
|
|
344
|
+
registry.add(toDisposable(() => {
|
|
345
|
+
win.removeListener("resize", resizeMainWebView);
|
|
346
|
+
}));
|
|
207
347
|
if (state.onClose) {
|
|
208
|
-
|
|
348
|
+
const onCloseHandler = (event) => {
|
|
209
349
|
void state.onClose?.(event);
|
|
210
|
-
}
|
|
350
|
+
};
|
|
351
|
+
win.on("close", onCloseHandler);
|
|
352
|
+
registry.add(toDisposable(() => {
|
|
353
|
+
win.removeListener("close", onCloseHandler);
|
|
354
|
+
}));
|
|
355
|
+
}
|
|
356
|
+
const devToolsAccelerator = "CommandOrControl+Shift+I";
|
|
357
|
+
const registered = globalShortcut2.register(devToolsAccelerator, () => {
|
|
358
|
+
win.webContents.openDevTools({ mode: "detach" });
|
|
359
|
+
});
|
|
360
|
+
if (registered) {
|
|
361
|
+
registry.add(toDisposable(() => {
|
|
362
|
+
globalShortcut2.unregister(devToolsAccelerator);
|
|
363
|
+
}));
|
|
211
364
|
}
|
|
365
|
+
return registry;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/main/utils/sender-policy.ts
|
|
369
|
+
function createWorkbenchSenderPolicy(ctx) {
|
|
370
|
+
return (sender) => {
|
|
371
|
+
if (sender.isDestroyed()) return false;
|
|
372
|
+
if (ctx.windows.isMainSender(sender.id)) return true;
|
|
373
|
+
if (ctx.windows.isSettingsWindowSender(sender.id)) return true;
|
|
374
|
+
const settingsViewId = ctx.views.getSettingsWebContentsId();
|
|
375
|
+
if (settingsViewId != null && sender.id === settingsViewId) return true;
|
|
376
|
+
const popoverViewId = ctx.views.getPopoverWebContentsId();
|
|
377
|
+
if (popoverViewId != null && sender.id === popoverViewId) return true;
|
|
378
|
+
return false;
|
|
379
|
+
};
|
|
212
380
|
}
|
|
213
381
|
|
|
214
382
|
// src/main/services/default-adapter.ts
|
|
215
|
-
import
|
|
383
|
+
import path5 from "path";
|
|
216
384
|
import { app as app5 } from "electron";
|
|
217
385
|
var defaultAdapter = {
|
|
218
386
|
async openProject(opts) {
|
|
219
387
|
const diminaKit = await import("@dimina-kit/devkit");
|
|
220
388
|
const openProject = diminaKit.openProject;
|
|
221
389
|
return openProject({
|
|
222
|
-
outputDir:
|
|
390
|
+
outputDir: path5.join(app5.getPath("userData"), "dimina-fe-output"),
|
|
223
391
|
...opts,
|
|
224
392
|
simulatorDir
|
|
225
393
|
});
|
|
@@ -237,8 +405,17 @@ var SimulatorChannel = {
|
|
|
237
405
|
AppData: "simulator:appdata",
|
|
238
406
|
AppDataAll: "simulator:appdata-all"
|
|
239
407
|
};
|
|
408
|
+
var SimulatorCustomApiChannel = {
|
|
409
|
+
List: "simulator:custom-apis:list",
|
|
410
|
+
Invoke: "simulator:custom-apis:invoke"
|
|
411
|
+
};
|
|
240
412
|
var SimulatorStorageChannel = {
|
|
241
413
|
GetSnapshot: "simulator:storage:snapshot",
|
|
414
|
+
GetActivePrefix: "simulator:storage:activePrefix",
|
|
415
|
+
Set: "simulator:storage:set",
|
|
416
|
+
Remove: "simulator:storage:remove",
|
|
417
|
+
Clear: "simulator:storage:clear",
|
|
418
|
+
ClearAll: "simulator:storage:clearAll",
|
|
242
419
|
Event: "simulator:storage:event"
|
|
243
420
|
};
|
|
244
421
|
var SimulatorElementChannel = {
|
|
@@ -311,6 +488,13 @@ var SettingsChannel = {
|
|
|
311
488
|
Closed: "settings:closed",
|
|
312
489
|
Changed: "settings:changed"
|
|
313
490
|
};
|
|
491
|
+
var UpdateChannel = {
|
|
492
|
+
Check: "updates:check",
|
|
493
|
+
Download: "updates:download",
|
|
494
|
+
Install: "updates:install",
|
|
495
|
+
DownloadProgress: "updates:downloadProgress",
|
|
496
|
+
Available: "updates:available"
|
|
497
|
+
};
|
|
314
498
|
|
|
315
499
|
// src/main/services/notifications/renderer-notifier.ts
|
|
316
500
|
function liveWebContents(wc) {
|
|
@@ -371,7 +555,7 @@ function createRendererNotifier(ctx) {
|
|
|
371
555
|
|
|
372
556
|
// src/main/services/views/view-manager.ts
|
|
373
557
|
import { WebContentsView as WebContentsView2, webContents } from "electron";
|
|
374
|
-
import
|
|
558
|
+
import path6 from "path";
|
|
375
559
|
|
|
376
560
|
// src/main/services/layout/index.ts
|
|
377
561
|
var HEADER_H = 40;
|
|
@@ -530,10 +714,16 @@ function createViewManager(ctx) {
|
|
|
530
714
|
async function showSettings() {
|
|
531
715
|
if (!settingsView) {
|
|
532
716
|
settingsView = new WebContentsView2({
|
|
533
|
-
webPreferences: {
|
|
717
|
+
webPreferences: {
|
|
718
|
+
nodeIntegration: false,
|
|
719
|
+
contextIsolation: true,
|
|
720
|
+
sandbox: false,
|
|
721
|
+
preload: mainPreloadPath
|
|
722
|
+
}
|
|
534
723
|
});
|
|
724
|
+
applyNavigationHardening(settingsView.webContents, ctx.rendererDir);
|
|
535
725
|
await settingsView.webContents.loadFile(
|
|
536
|
-
|
|
726
|
+
path6.join(ctx.rendererDir, "entries/settings/index.html")
|
|
537
727
|
);
|
|
538
728
|
}
|
|
539
729
|
if (!settingsViewAdded) {
|
|
@@ -554,8 +744,14 @@ function createViewManager(ctx) {
|
|
|
554
744
|
function showPopover(data) {
|
|
555
745
|
hidePopover();
|
|
556
746
|
const popover = new WebContentsView2({
|
|
557
|
-
webPreferences: {
|
|
747
|
+
webPreferences: {
|
|
748
|
+
nodeIntegration: false,
|
|
749
|
+
contextIsolation: true,
|
|
750
|
+
sandbox: false,
|
|
751
|
+
preload: mainPreloadPath
|
|
752
|
+
}
|
|
558
753
|
});
|
|
754
|
+
applyNavigationHardening(popover.webContents, ctx.rendererDir);
|
|
559
755
|
popoverView = popover;
|
|
560
756
|
popover.setBackgroundColor("#00000000");
|
|
561
757
|
ctx.mainWindow.contentView.addChildView(popover);
|
|
@@ -564,7 +760,7 @@ function createViewManager(ctx) {
|
|
|
564
760
|
ctx.notify.popoverInit(popover, data);
|
|
565
761
|
});
|
|
566
762
|
popover.webContents.loadFile(
|
|
567
|
-
|
|
763
|
+
path6.join(ctx.rendererDir, "entries/popover/index.html")
|
|
568
764
|
);
|
|
569
765
|
}
|
|
570
766
|
function hidePopover() {
|
|
@@ -630,14 +826,50 @@ function createViewManager(ctx) {
|
|
|
630
826
|
if (settingsView.webContents.isDestroyed()) return null;
|
|
631
827
|
return settingsView.webContents;
|
|
632
828
|
},
|
|
829
|
+
getSettingsWebContentsId: () => {
|
|
830
|
+
if (!settingsView) return null;
|
|
831
|
+
if (settingsView.webContents.isDestroyed()) return null;
|
|
832
|
+
return settingsView.webContents.id;
|
|
833
|
+
},
|
|
834
|
+
getPopoverWebContentsId: () => {
|
|
835
|
+
if (!popoverView) return null;
|
|
836
|
+
if (popoverView.webContents.isDestroyed()) return null;
|
|
837
|
+
return popoverView.webContents.id;
|
|
838
|
+
},
|
|
633
839
|
resize,
|
|
634
840
|
setVisible
|
|
635
841
|
};
|
|
636
842
|
}
|
|
637
843
|
|
|
844
|
+
// src/main/services/window-service.ts
|
|
845
|
+
function createWindowService(mainWindow) {
|
|
846
|
+
let settingsWin = null;
|
|
847
|
+
return {
|
|
848
|
+
get mainWindow() {
|
|
849
|
+
return mainWindow;
|
|
850
|
+
},
|
|
851
|
+
get settingsWindow() {
|
|
852
|
+
return settingsWin;
|
|
853
|
+
},
|
|
854
|
+
setSettingsWindow(win) {
|
|
855
|
+
settingsWin = win;
|
|
856
|
+
},
|
|
857
|
+
closeSettingsWindow() {
|
|
858
|
+
if (settingsWin && !settingsWin.isDestroyed()) settingsWin.close();
|
|
859
|
+
settingsWin = null;
|
|
860
|
+
},
|
|
861
|
+
isMainSender(id) {
|
|
862
|
+
return !mainWindow.isDestroyed() && mainWindow.webContents.id === id;
|
|
863
|
+
},
|
|
864
|
+
isSettingsWindowSender(id) {
|
|
865
|
+
return !!settingsWin && !settingsWin.isDestroyed() && settingsWin.webContents.id === id;
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
|
|
638
870
|
// src/main/services/projects/project-repository.ts
|
|
639
871
|
import { app as app6 } from "electron";
|
|
640
|
-
import
|
|
872
|
+
import path7 from "path";
|
|
641
873
|
import fs3 from "fs";
|
|
642
874
|
|
|
643
875
|
// src/shared/constants.ts
|
|
@@ -684,7 +916,7 @@ function createLogger(tag) {
|
|
|
684
916
|
// src/main/services/projects/project-repository.ts
|
|
685
917
|
var log = createLogger("projects");
|
|
686
918
|
function getProjectsFile() {
|
|
687
|
-
return
|
|
919
|
+
return path7.join(app6.getPath("userData"), "dimina-projects.json");
|
|
688
920
|
}
|
|
689
921
|
function load() {
|
|
690
922
|
try {
|
|
@@ -706,13 +938,13 @@ function validateProjectDir(dirPath) {
|
|
|
706
938
|
if (!fs3.existsSync(dirPath)) {
|
|
707
939
|
return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
|
|
708
940
|
}
|
|
709
|
-
if (!fs3.existsSync(
|
|
710
|
-
const configPath =
|
|
941
|
+
if (!fs3.existsSync(path7.join(dirPath, "app.json"))) {
|
|
942
|
+
const configPath = path7.join(dirPath, "project.config.json");
|
|
711
943
|
if (fs3.existsSync(configPath)) {
|
|
712
944
|
try {
|
|
713
945
|
const cfg = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
714
946
|
if (cfg.miniprogramRoot) {
|
|
715
|
-
const resolvedRoot =
|
|
947
|
+
const resolvedRoot = path7.resolve(dirPath, cfg.miniprogramRoot);
|
|
716
948
|
return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
|
|
717
949
|
}
|
|
718
950
|
} catch (err) {
|
|
@@ -723,11 +955,14 @@ function validateProjectDir(dirPath) {
|
|
|
723
955
|
}
|
|
724
956
|
return null;
|
|
725
957
|
}
|
|
958
|
+
function hasProject(dirPath) {
|
|
959
|
+
return load().some((p) => p.path === dirPath);
|
|
960
|
+
}
|
|
726
961
|
function addProject(dirPath) {
|
|
727
962
|
const projects = load();
|
|
728
|
-
let name =
|
|
963
|
+
let name = path7.basename(dirPath);
|
|
729
964
|
try {
|
|
730
|
-
const configPath =
|
|
965
|
+
const configPath = path7.join(dirPath, "project.config.json");
|
|
731
966
|
if (fs3.existsSync(configPath)) {
|
|
732
967
|
const cfg = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
733
968
|
if (cfg.projectname) name = cfg.projectname;
|
|
@@ -766,7 +1001,7 @@ function getCompileConfig(dirPath) {
|
|
|
766
1001
|
};
|
|
767
1002
|
}
|
|
768
1003
|
function getProjectPages(dirPath) {
|
|
769
|
-
const appJsonPath =
|
|
1004
|
+
const appJsonPath = path7.join(dirPath, "app.json");
|
|
770
1005
|
try {
|
|
771
1006
|
const appJson = JSON.parse(
|
|
772
1007
|
fs3.readFileSync(appJsonPath, "utf-8")
|
|
@@ -792,7 +1027,7 @@ function getProjectSettings(projectPath) {
|
|
|
792
1027
|
return { uploadWithSourceMap: false };
|
|
793
1028
|
}
|
|
794
1029
|
try {
|
|
795
|
-
const configPath =
|
|
1030
|
+
const configPath = path7.join(projectPath, "project.config.json");
|
|
796
1031
|
const config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
797
1032
|
return {
|
|
798
1033
|
uploadWithSourceMap: !!config.setting?.uploadWithSourceMap
|
|
@@ -803,7 +1038,7 @@ function getProjectSettings(projectPath) {
|
|
|
803
1038
|
}
|
|
804
1039
|
function updateProjectSettings(projectPath, patch) {
|
|
805
1040
|
if (!projectPath) return;
|
|
806
|
-
const configPath =
|
|
1041
|
+
const configPath = path7.join(projectPath, "project.config.json");
|
|
807
1042
|
let config = {};
|
|
808
1043
|
try {
|
|
809
1044
|
config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
@@ -823,8 +1058,8 @@ function updateProjectSettings(projectPath, patch) {
|
|
|
823
1058
|
function createWorkspaceService(ctx) {
|
|
824
1059
|
let currentSession = null;
|
|
825
1060
|
let currentProjectPath = "";
|
|
826
|
-
function sendStatus(status, message) {
|
|
827
|
-
ctx.notify.projectStatus({ status, message });
|
|
1061
|
+
function sendStatus(status, message, hotReload) {
|
|
1062
|
+
ctx.notify.projectStatus(hotReload ? { status, message, hotReload: true } : { status, message });
|
|
828
1063
|
}
|
|
829
1064
|
function bestEffort(label, fn) {
|
|
830
1065
|
try {
|
|
@@ -861,6 +1096,7 @@ function createWorkspaceService(ctx) {
|
|
|
861
1096
|
listProjects: () => listProjects(),
|
|
862
1097
|
addProject: (dirPath) => addProject(dirPath),
|
|
863
1098
|
removeProject: (dirPath) => removeProject(dirPath),
|
|
1099
|
+
hasProject: (dirPath) => hasProject(dirPath),
|
|
864
1100
|
validateProjectDir: (dirPath) => validateProjectDir(dirPath),
|
|
865
1101
|
async openProject(projectPath) {
|
|
866
1102
|
clearSimulatorServicewechatReferer();
|
|
@@ -872,12 +1108,14 @@ function createWorkspaceService(ctx) {
|
|
|
872
1108
|
return { success: false, error: dirError };
|
|
873
1109
|
}
|
|
874
1110
|
sendStatus("compiling", "\u6B63\u5728\u7F16\u8BD1...");
|
|
1111
|
+
const { compile } = loadWorkbenchSettings();
|
|
875
1112
|
let session2;
|
|
876
1113
|
try {
|
|
877
1114
|
session2 = await ctx.adapter.openProject({
|
|
878
1115
|
projectPath,
|
|
879
1116
|
sourcemap: true,
|
|
880
|
-
|
|
1117
|
+
watch: compile.watch,
|
|
1118
|
+
onRebuild: () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u70ED\u66F4\u65B0", true),
|
|
881
1119
|
onBuildError: (err) => sendStatus("error", String(err))
|
|
882
1120
|
});
|
|
883
1121
|
} catch (err) {
|
|
@@ -935,9 +1173,12 @@ function createWorkbenchContext(opts) {
|
|
|
935
1173
|
brandingProvider: opts.brandingProvider,
|
|
936
1174
|
workbenchSettingsWindow: null
|
|
937
1175
|
};
|
|
1176
|
+
ctx.registry = new DisposableRegistry();
|
|
1177
|
+
ctx.windows = createWindowService(ctx.mainWindow);
|
|
938
1178
|
ctx.views = createViewManager(ctx);
|
|
939
1179
|
ctx.notify = createRendererNotifier(ctx);
|
|
940
1180
|
ctx.workspace = createWorkspaceService(ctx);
|
|
1181
|
+
ctx.senderPolicy = createWorkbenchSenderPolicy(ctx);
|
|
941
1182
|
return ctx;
|
|
942
1183
|
}
|
|
943
1184
|
|
|
@@ -1008,56 +1249,246 @@ function installAppMenu(ctx) {
|
|
|
1008
1249
|
}
|
|
1009
1250
|
|
|
1010
1251
|
// src/main/ipc/app.ts
|
|
1011
|
-
import { ipcMain } from "electron";
|
|
1012
1252
|
import fs4 from "fs";
|
|
1253
|
+
|
|
1254
|
+
// src/main/utils/ipc-registry.ts
|
|
1255
|
+
import { ipcMain } from "electron";
|
|
1256
|
+
|
|
1257
|
+
// src/main/utils/ipc-schema.ts
|
|
1258
|
+
var IpcValidationError = class extends Error {
|
|
1259
|
+
channel;
|
|
1260
|
+
paths;
|
|
1261
|
+
constructor(channel, issues) {
|
|
1262
|
+
const paths = issues.map((i) => i.path.map((p) => String(p)).join(".") || "<root>");
|
|
1263
|
+
super(`Invalid IPC arguments for ${channel}`);
|
|
1264
|
+
this.name = "IpcValidationError";
|
|
1265
|
+
this.channel = channel;
|
|
1266
|
+
this.paths = paths;
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
function validate(channel, schema, args) {
|
|
1270
|
+
const r = schema.safeParse(args);
|
|
1271
|
+
if (!r.success) {
|
|
1272
|
+
console.warn("[ipc] schema reject", channel, r.error.issues);
|
|
1273
|
+
throw new IpcValidationError(channel, r.error.issues);
|
|
1274
|
+
}
|
|
1275
|
+
return r.data;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// src/main/utils/ipc-registry.ts
|
|
1279
|
+
var log2 = createLogger("ipc");
|
|
1280
|
+
function reportListenerError(channel, err) {
|
|
1281
|
+
if (err instanceof IpcValidationError) {
|
|
1282
|
+
log2.warn(`schema reject on '${channel}' at [${err.paths.join(", ")}]`);
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
if (err instanceof Error) {
|
|
1286
|
+
log2.error(`listener threw on '${channel}': ${err.message}`, err.stack);
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
log2.error(`listener threw on '${channel}'`, err);
|
|
1290
|
+
}
|
|
1291
|
+
function isThenable(v) {
|
|
1292
|
+
return v != null && (typeof v === "object" || typeof v === "function") && typeof v.then === "function";
|
|
1293
|
+
}
|
|
1294
|
+
function summarizeSender(sender) {
|
|
1295
|
+
if (sender.isDestroyed()) return "<destroyed>";
|
|
1296
|
+
const url = sender.getURL();
|
|
1297
|
+
return `id=${sender.id} ${url.slice(0, 120)}`;
|
|
1298
|
+
}
|
|
1299
|
+
var IpcRegistry = class {
|
|
1300
|
+
constructor(policy) {
|
|
1301
|
+
this.policy = policy;
|
|
1302
|
+
}
|
|
1303
|
+
policy;
|
|
1304
|
+
registry = new DisposableRegistry();
|
|
1305
|
+
handle(channel, fn) {
|
|
1306
|
+
const policy = this.policy;
|
|
1307
|
+
const guarded = policy ? (event, ...args) => {
|
|
1308
|
+
const sender = event.sender;
|
|
1309
|
+
if (!policy(sender)) {
|
|
1310
|
+
console.warn(
|
|
1311
|
+
`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`
|
|
1312
|
+
);
|
|
1313
|
+
throw new Error(`IPC sender rejected for channel ${channel}`);
|
|
1314
|
+
}
|
|
1315
|
+
return fn(event, ...args);
|
|
1316
|
+
} : fn;
|
|
1317
|
+
ipcMain.handle(channel, guarded);
|
|
1318
|
+
this.registry.add(() => ipcMain.removeHandler(channel));
|
|
1319
|
+
return this;
|
|
1320
|
+
}
|
|
1321
|
+
on(channel, fn) {
|
|
1322
|
+
const policy = this.policy;
|
|
1323
|
+
const raw = fn;
|
|
1324
|
+
const safeInvoke = (event, args) => {
|
|
1325
|
+
try {
|
|
1326
|
+
const ret = raw(event, ...args);
|
|
1327
|
+
if (isThenable(ret)) {
|
|
1328
|
+
Promise.resolve(ret).catch((err) => reportListenerError(channel, err));
|
|
1329
|
+
}
|
|
1330
|
+
} catch (err) {
|
|
1331
|
+
reportListenerError(channel, err);
|
|
1332
|
+
}
|
|
1333
|
+
};
|
|
1334
|
+
const guarded = policy ? (event, ...args) => {
|
|
1335
|
+
const sender = event.sender;
|
|
1336
|
+
if (!policy(sender)) {
|
|
1337
|
+
console.warn(
|
|
1338
|
+
`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`
|
|
1339
|
+
);
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
safeInvoke(event, args);
|
|
1343
|
+
} : (event, ...args) => safeInvoke(event, args);
|
|
1344
|
+
ipcMain.on(channel, guarded);
|
|
1345
|
+
this.registry.add(() => {
|
|
1346
|
+
ipcMain.removeListener(channel, guarded);
|
|
1347
|
+
});
|
|
1348
|
+
return this;
|
|
1349
|
+
}
|
|
1350
|
+
dispose() {
|
|
1351
|
+
return this.registry.dispose();
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
|
|
1355
|
+
// src/main/ipc/app.ts
|
|
1013
1356
|
function registerAppIpc(ctx) {
|
|
1014
|
-
|
|
1357
|
+
return new IpcRegistry(ctx.senderPolicy).handle(AppChannel.GetPreloadPath, () => {
|
|
1015
1358
|
return `file://${fs4.realpathSync(ctx.preloadPath)}`;
|
|
1016
|
-
})
|
|
1017
|
-
ipcMain.handle(AppChannel.GetBranding, async () => {
|
|
1359
|
+
}).handle(AppChannel.GetBranding, async () => {
|
|
1018
1360
|
if (ctx.brandingProvider) return ctx.brandingProvider();
|
|
1019
1361
|
return { appName: ctx.appName };
|
|
1020
1362
|
});
|
|
1021
1363
|
}
|
|
1022
1364
|
|
|
1365
|
+
// src/shared/ipc-schemas.ts
|
|
1366
|
+
import { z } from "zod";
|
|
1367
|
+
var AbsolutePath = z.string().min(1).refine(
|
|
1368
|
+
(p) => p.startsWith("/") || /^[a-zA-Z]:\\/.test(p) || /^[a-zA-Z]:\//.test(p),
|
|
1369
|
+
"must be an absolute path"
|
|
1370
|
+
);
|
|
1371
|
+
var PanelEvalSchema = z.tuple([z.string().min(1).max(5e4)]);
|
|
1372
|
+
var ProjectsAddSchema = z.tuple([AbsolutePath]);
|
|
1373
|
+
var ProjectOpenSchema = z.tuple([AbsolutePath]);
|
|
1374
|
+
var CompileConfigShape = z.looseObject({
|
|
1375
|
+
startPage: z.string().optional(),
|
|
1376
|
+
scene: z.number().optional(),
|
|
1377
|
+
queryParams: z.array(z.looseObject({ key: z.string(), value: z.string() })).optional()
|
|
1378
|
+
});
|
|
1379
|
+
var ProjectSaveCompileConfigSchema = z.tuple([
|
|
1380
|
+
AbsolutePath,
|
|
1381
|
+
CompileConfigShape
|
|
1382
|
+
]);
|
|
1383
|
+
var PopoverShowSchema = z.tuple([z.looseObject({})]);
|
|
1384
|
+
var SimWidth = z.number().int().min(100).max(2e3);
|
|
1385
|
+
var SimulatorAttachSchema = z.tuple([
|
|
1386
|
+
z.number().int().positive(),
|
|
1387
|
+
SimWidth
|
|
1388
|
+
]);
|
|
1389
|
+
var SimulatorResizeSchema = z.tuple([SimWidth]);
|
|
1390
|
+
var SimulatorSetVisibleSchema = z.tuple([z.boolean(), SimWidth]);
|
|
1391
|
+
var SimulatorCustomApiInvokeSchema = z.tuple([
|
|
1392
|
+
z.string().min(1).max(256),
|
|
1393
|
+
z.unknown()
|
|
1394
|
+
]);
|
|
1395
|
+
var ProjectGetPagesSchema = z.tuple([AbsolutePath]);
|
|
1396
|
+
var ProjectGetCompileConfigSchema = z.tuple([AbsolutePath]);
|
|
1397
|
+
var ProjectsRemoveSchema = z.tuple([AbsolutePath]);
|
|
1398
|
+
var WorkbenchSettingsSaveSchema = z.tuple([
|
|
1399
|
+
z.looseObject({
|
|
1400
|
+
cdp: z.looseObject({
|
|
1401
|
+
enabled: z.boolean(),
|
|
1402
|
+
port: z.number().int().min(0).max(65535)
|
|
1403
|
+
}),
|
|
1404
|
+
mcp: z.looseObject({
|
|
1405
|
+
enabled: z.boolean(),
|
|
1406
|
+
port: z.number().int().min(0).max(65535)
|
|
1407
|
+
}),
|
|
1408
|
+
compile: z.looseObject({
|
|
1409
|
+
watch: z.boolean()
|
|
1410
|
+
}),
|
|
1411
|
+
theme: z.enum(["system", "light", "dark"])
|
|
1412
|
+
})
|
|
1413
|
+
]);
|
|
1414
|
+
var WorkbenchSettingsSetThemeSchema = z.tuple([
|
|
1415
|
+
z.enum(["system", "light", "dark"])
|
|
1416
|
+
]);
|
|
1417
|
+
var WorkbenchSettingsSetVisibleSchema = z.tuple([z.boolean()]);
|
|
1418
|
+
var SettingsSetVisibleSchema = z.tuple([z.boolean()]);
|
|
1419
|
+
var SettingsConfigChangedSchema = z.tuple([CompileConfigShape]);
|
|
1420
|
+
var PopoverRelaunchSchema = z.tuple([CompileConfigShape]);
|
|
1421
|
+
var SettingsProjectSettingsChangedSchema = z.tuple([
|
|
1422
|
+
z.looseObject({
|
|
1423
|
+
uploadWithSourceMap: z.boolean().optional()
|
|
1424
|
+
})
|
|
1425
|
+
]);
|
|
1426
|
+
var PanelSelectSchema = z.tuple([z.string().min(1).max(200)]);
|
|
1427
|
+
|
|
1428
|
+
// src/main/services/simulator/custom-apis.ts
|
|
1429
|
+
function createSimulatorApiRegistry() {
|
|
1430
|
+
const handlers2 = /* @__PURE__ */ new Map();
|
|
1431
|
+
return {
|
|
1432
|
+
register(name, handler) {
|
|
1433
|
+
handlers2.set(name, handler);
|
|
1434
|
+
return () => {
|
|
1435
|
+
if (handlers2.get(name) === handler) handlers2.delete(name);
|
|
1436
|
+
};
|
|
1437
|
+
},
|
|
1438
|
+
list() {
|
|
1439
|
+
return Array.from(handlers2.keys());
|
|
1440
|
+
},
|
|
1441
|
+
async invoke(name, params) {
|
|
1442
|
+
const handler = handlers2.get(name);
|
|
1443
|
+
if (!handler) throw new Error(`Simulator API "${name}" is not registered`);
|
|
1444
|
+
return await handler(params);
|
|
1445
|
+
},
|
|
1446
|
+
clear() {
|
|
1447
|
+
handlers2.clear();
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
var simulatorApiRegistry = createSimulatorApiRegistry();
|
|
1452
|
+
|
|
1023
1453
|
// src/main/ipc/simulator.ts
|
|
1024
|
-
import { ipcMain as ipcMain2 } from "electron";
|
|
1025
1454
|
function registerSimulatorIpc(ctx) {
|
|
1026
|
-
|
|
1455
|
+
return new IpcRegistry(ctx.senderPolicy).handle(SimulatorChannel.Attach, (_, ...args) => {
|
|
1456
|
+
const [simWcId, simWidth] = validate(SimulatorChannel.Attach, SimulatorAttachSchema, args);
|
|
1027
1457
|
ctx.views.attachSimulator(simWcId, simWidth);
|
|
1028
|
-
})
|
|
1029
|
-
ipcMain2.handle(SimulatorChannel.Detach, () => {
|
|
1458
|
+
}).handle(SimulatorChannel.Detach, () => {
|
|
1030
1459
|
ctx.views.detachSimulator();
|
|
1031
|
-
})
|
|
1032
|
-
|
|
1460
|
+
}).handle(SimulatorChannel.Resize, (_, ...args) => {
|
|
1461
|
+
const [simWidth] = validate(SimulatorChannel.Resize, SimulatorResizeSchema, args);
|
|
1033
1462
|
ctx.views.resize(simWidth);
|
|
1034
|
-
})
|
|
1035
|
-
|
|
1463
|
+
}).handle(SimulatorChannel.SetVisible, (_, ...args) => {
|
|
1464
|
+
const [visible, simWidth] = validate(SimulatorChannel.SetVisible, SimulatorSetVisibleSchema, args);
|
|
1036
1465
|
ctx.views.setVisible(visible, simWidth);
|
|
1037
|
-
})
|
|
1038
|
-
|
|
1466
|
+
}).handle(SimulatorCustomApiChannel.List, () => {
|
|
1467
|
+
return simulatorApiRegistry.list();
|
|
1468
|
+
}).handle(SimulatorCustomApiChannel.Invoke, (_, ...args) => {
|
|
1469
|
+
const [name, params] = validate(SimulatorCustomApiChannel.Invoke, SimulatorCustomApiInvokeSchema, args);
|
|
1470
|
+
return simulatorApiRegistry.invoke(name, params);
|
|
1471
|
+
}).handle(WorkbenchChannel.GetPanelConfig, () => {
|
|
1039
1472
|
return ctx.panels;
|
|
1040
|
-
})
|
|
1041
|
-
ipcMain2.handle(WorkbenchChannel.GetApiNamespaces, () => {
|
|
1473
|
+
}).handle(WorkbenchChannel.GetApiNamespaces, () => {
|
|
1042
1474
|
return ctx.apiNamespaces;
|
|
1043
|
-
})
|
|
1044
|
-
ipcMain2.on(WorkbenchChannel.Reset, () => {
|
|
1475
|
+
}).on(WorkbenchChannel.Reset, () => {
|
|
1045
1476
|
ctx.notify.workbenchReset();
|
|
1046
1477
|
});
|
|
1047
1478
|
}
|
|
1048
1479
|
|
|
1049
1480
|
// src/main/ipc/panels.ts
|
|
1050
|
-
import {
|
|
1481
|
+
import { webContents as webContents2 } from "electron";
|
|
1051
1482
|
var BUILTIN_PANELS = [
|
|
1052
1483
|
{ id: "wxml", label: "WXML" },
|
|
1053
1484
|
{ id: "appdata", label: "AppData" },
|
|
1054
1485
|
{ id: "storage", label: "Storage" }
|
|
1055
1486
|
];
|
|
1056
1487
|
function registerPanelsIpc(ctx) {
|
|
1057
|
-
|
|
1488
|
+
return new IpcRegistry(ctx.senderPolicy).handle(PanelChannel.List, () => {
|
|
1058
1489
|
return BUILTIN_PANELS.filter((p) => ctx.panels.includes(p.id));
|
|
1059
|
-
})
|
|
1060
|
-
|
|
1490
|
+
}).handle(PanelChannel.Eval, async (_event, ...args) => {
|
|
1491
|
+
const [expression] = validate(PanelChannel.Eval, PanelEvalSchema, args);
|
|
1061
1492
|
const simWcId = ctx.views.getSimulatorWebContentsId();
|
|
1062
1493
|
if (!ctx.workspace.hasActiveSession() || simWcId == null) return void 0;
|
|
1063
1494
|
const sim = webContents2.fromId(simWcId);
|
|
@@ -1067,13 +1498,12 @@ function registerPanelsIpc(ctx) {
|
|
|
1067
1498
|
} catch {
|
|
1068
1499
|
return void 0;
|
|
1069
1500
|
}
|
|
1070
|
-
})
|
|
1071
|
-
|
|
1501
|
+
}).handle(PanelChannel.Select, (_event, ...args) => {
|
|
1502
|
+
validate(PanelChannel.Select, PanelSelectSchema, args);
|
|
1072
1503
|
if (ctx.views.hasSimulatorView() && ctx.views.isSimulatorAdded()) {
|
|
1073
1504
|
ctx.views.hideSimulator();
|
|
1074
1505
|
}
|
|
1075
|
-
})
|
|
1076
|
-
ipcMain3.handle(PanelChannel.SelectSimulator, () => {
|
|
1506
|
+
}).handle(PanelChannel.SelectSimulator, () => {
|
|
1077
1507
|
if (ctx.views.hasSimulatorView() && !ctx.views.isSimulatorAdded()) {
|
|
1078
1508
|
ctx.views.showSimulator(ctx.views.getLastSimWidth());
|
|
1079
1509
|
}
|
|
@@ -1081,76 +1511,119 @@ function registerPanelsIpc(ctx) {
|
|
|
1081
1511
|
}
|
|
1082
1512
|
|
|
1083
1513
|
// src/main/ipc/popover.ts
|
|
1084
|
-
import { ipcMain as ipcMain4 } from "electron";
|
|
1085
1514
|
function registerPopoverIpc(ctx) {
|
|
1086
|
-
|
|
1515
|
+
return new IpcRegistry(ctx.senderPolicy).handle(PopoverChannel.Show, (_event, ...args) => {
|
|
1516
|
+
const [data] = validate(PopoverChannel.Show, PopoverShowSchema, args);
|
|
1087
1517
|
ctx.views.showPopover(data);
|
|
1088
|
-
})
|
|
1089
|
-
|
|
1090
|
-
|
|
1518
|
+
}).handle(PopoverChannel.Hide, () => {
|
|
1519
|
+
ctx.views.hidePopover();
|
|
1520
|
+
}).on(PopoverChannel.Relaunch, (_event, ...args) => {
|
|
1521
|
+
const [newConfig] = validate(PopoverChannel.Relaunch, PopoverRelaunchSchema, args);
|
|
1091
1522
|
ctx.views.hidePopover();
|
|
1092
1523
|
ctx.notify.popoverRelaunch(newConfig);
|
|
1093
1524
|
});
|
|
1094
1525
|
}
|
|
1526
|
+
var popoverModule = {
|
|
1527
|
+
setup: (ctx) => registerPopoverIpc(ctx)
|
|
1528
|
+
};
|
|
1095
1529
|
|
|
1096
1530
|
// src/main/ipc/projects.ts
|
|
1097
|
-
import {
|
|
1531
|
+
import { dialog } from "electron";
|
|
1098
1532
|
function registerProjectsIpc(ctx) {
|
|
1099
|
-
|
|
1100
|
-
|
|
1533
|
+
return new IpcRegistry(ctx.senderPolicy).handle(ProjectsChannel.List, () => {
|
|
1534
|
+
return ctx.workspace.listProjects();
|
|
1535
|
+
}).handle(DialogChannel.OpenDirectory, async () => {
|
|
1101
1536
|
const result = await dialog.showOpenDialog(ctx.mainWindow, {
|
|
1102
1537
|
properties: ["openDirectory"],
|
|
1103
1538
|
title: "\u9009\u62E9\u5C0F\u7A0B\u5E8F\u9879\u76EE\u76EE\u5F55"
|
|
1104
1539
|
});
|
|
1105
1540
|
return result.canceled ? null : result.filePaths[0];
|
|
1541
|
+
}).handle(ProjectsChannel.Add, async (_event, ...args) => {
|
|
1542
|
+
const [dirPath] = validate(ProjectsChannel.Add, ProjectsAddSchema, args);
|
|
1543
|
+
const dirError = ctx.workspace.validateProjectDir(dirPath);
|
|
1544
|
+
if (dirError) {
|
|
1545
|
+
await dialog.showMessageBox(ctx.mainWindow, {
|
|
1546
|
+
type: "error",
|
|
1547
|
+
title: "\u65E0\u6CD5\u5BFC\u5165\u9879\u76EE",
|
|
1548
|
+
message: "\u8BE5\u76EE\u5F55\u4E0D\u662F\u6709\u6548\u7684\u5C0F\u7A0B\u5E8F\u9879\u76EE",
|
|
1549
|
+
detail: dirError,
|
|
1550
|
+
buttons: ["\u786E\u5B9A"]
|
|
1551
|
+
});
|
|
1552
|
+
throw new Error(dirError);
|
|
1553
|
+
}
|
|
1554
|
+
const duplicate = ctx.workspace.hasProject(dirPath);
|
|
1555
|
+
const project = ctx.workspace.addProject(dirPath);
|
|
1556
|
+
if (duplicate) {
|
|
1557
|
+
await dialog.showMessageBox(ctx.mainWindow, {
|
|
1558
|
+
type: "info",
|
|
1559
|
+
title: "\u9879\u76EE\u5DF2\u5B58\u5728",
|
|
1560
|
+
message: "\u8BE5\u9879\u76EE\u5DF2\u5728\u5217\u8868\u4E2D",
|
|
1561
|
+
detail: dirPath,
|
|
1562
|
+
buttons: ["\u786E\u5B9A"]
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
return project;
|
|
1566
|
+
}).handle(ProjectsChannel.Remove, (_event, ...args) => {
|
|
1567
|
+
const [dirPath] = validate(ProjectsChannel.Remove, ProjectsRemoveSchema, args);
|
|
1568
|
+
return ctx.workspace.removeProject(dirPath);
|
|
1106
1569
|
});
|
|
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
1570
|
}
|
|
1571
|
+
var projectsModule = {
|
|
1572
|
+
setup: (ctx) => registerProjectsIpc(ctx)
|
|
1573
|
+
};
|
|
1116
1574
|
|
|
1117
1575
|
// src/main/ipc/session.ts
|
|
1118
|
-
import { ipcMain as ipcMain6 } from "electron";
|
|
1119
1576
|
function registerSessionIpc(ctx) {
|
|
1120
|
-
|
|
1121
|
-
ProjectChannel.Open,
|
|
1122
|
-
|
|
1123
|
-
)
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1577
|
+
return new IpcRegistry(ctx.senderPolicy).handle(ProjectChannel.Open, (_, ...args) => {
|
|
1578
|
+
const [projectPath] = validate(ProjectChannel.Open, ProjectOpenSchema, args);
|
|
1579
|
+
return ctx.workspace.openProject(projectPath);
|
|
1580
|
+
}).handle(ProjectChannel.GetPages, (_, ...args) => {
|
|
1581
|
+
const [projectPath] = validate(ProjectChannel.GetPages, ProjectGetPagesSchema, args);
|
|
1582
|
+
return ctx.workspace.getProjectPages(projectPath);
|
|
1583
|
+
}).handle(ProjectChannel.GetCompileConfig, (_, ...args) => {
|
|
1584
|
+
const [projectPath] = validate(
|
|
1585
|
+
ProjectChannel.GetCompileConfig,
|
|
1586
|
+
ProjectGetCompileConfigSchema,
|
|
1587
|
+
args
|
|
1588
|
+
);
|
|
1589
|
+
return ctx.workspace.getCompileConfig(projectPath);
|
|
1590
|
+
}).handle(ProjectChannel.SaveCompileConfig, (_, ...args) => {
|
|
1591
|
+
const [projectPath, config] = validate(
|
|
1592
|
+
ProjectChannel.SaveCompileConfig,
|
|
1593
|
+
ProjectSaveCompileConfigSchema,
|
|
1594
|
+
args
|
|
1595
|
+
);
|
|
1596
|
+
return ctx.workspace.saveCompileConfig(projectPath, config);
|
|
1597
|
+
}).handle(ProjectChannel.Close, () => {
|
|
1598
|
+
return ctx.workspace.closeProject();
|
|
1599
|
+
});
|
|
1137
1600
|
}
|
|
1601
|
+
var sessionModule = {
|
|
1602
|
+
setup: (ctx) => registerSessionIpc(ctx)
|
|
1603
|
+
};
|
|
1138
1604
|
|
|
1139
1605
|
// src/main/ipc/settings.ts
|
|
1140
|
-
import { app as app7
|
|
1606
|
+
import { app as app7 } from "electron";
|
|
1141
1607
|
function registerSettingsIpc(ctx) {
|
|
1142
|
-
|
|
1608
|
+
return new IpcRegistry(ctx.senderPolicy).handle(WorkbenchSettingsChannel.Get, () => {
|
|
1143
1609
|
return loadWorkbenchSettings();
|
|
1144
|
-
})
|
|
1145
|
-
|
|
1610
|
+
}).handle(WorkbenchSettingsChannel.Save, (_, ...args) => {
|
|
1611
|
+
const [settings] = validate(
|
|
1612
|
+
WorkbenchSettingsChannel.Save,
|
|
1613
|
+
WorkbenchSettingsSaveSchema,
|
|
1614
|
+
args
|
|
1615
|
+
);
|
|
1146
1616
|
saveWorkbenchSettings(settings);
|
|
1147
1617
|
applyTheme(settings.theme ?? "system");
|
|
1148
1618
|
return { success: true };
|
|
1149
|
-
})
|
|
1150
|
-
|
|
1619
|
+
}).handle(WorkbenchSettingsChannel.SetTheme, (_, ...args) => {
|
|
1620
|
+
const [theme] = validate(
|
|
1621
|
+
WorkbenchSettingsChannel.SetTheme,
|
|
1622
|
+
WorkbenchSettingsSetThemeSchema,
|
|
1623
|
+
args
|
|
1624
|
+
);
|
|
1151
1625
|
applyTheme(theme);
|
|
1152
|
-
})
|
|
1153
|
-
ipcMain7.handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
|
|
1626
|
+
}).handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
|
|
1154
1627
|
const settings = loadWorkbenchSettings();
|
|
1155
1628
|
const switchValue = app7.commandLine.getSwitchValue("remote-debugging-port");
|
|
1156
1629
|
const implicitDevDefault = !app7.isPackaged && !settings.cdp.enabled && switchValue === "9222";
|
|
@@ -1161,15 +1634,19 @@ function registerSettingsIpc(ctx) {
|
|
|
1161
1634
|
activePort: switchValue ? parseInt(switchValue, 10) : null,
|
|
1162
1635
|
implicitDevDefault
|
|
1163
1636
|
};
|
|
1164
|
-
})
|
|
1165
|
-
|
|
1637
|
+
}).handle(WorkbenchSettingsChannel.SetVisible, async (_, ...args) => {
|
|
1638
|
+
const [visible] = validate(
|
|
1639
|
+
WorkbenchSettingsChannel.SetVisible,
|
|
1640
|
+
WorkbenchSettingsSetVisibleSchema,
|
|
1641
|
+
args
|
|
1642
|
+
);
|
|
1166
1643
|
if (visible) {
|
|
1167
1644
|
await openSettingsWindow(ctx);
|
|
1168
1645
|
} else {
|
|
1169
|
-
ctx.
|
|
1646
|
+
ctx.windows.closeSettingsWindow();
|
|
1170
1647
|
}
|
|
1171
|
-
})
|
|
1172
|
-
|
|
1648
|
+
}).handle(SettingsChannel.SetVisible, async (_, ...args) => {
|
|
1649
|
+
const [visible] = validate(SettingsChannel.SetVisible, SettingsSetVisibleSchema, args);
|
|
1173
1650
|
if (visible) {
|
|
1174
1651
|
await ctx.views.showSettings();
|
|
1175
1652
|
ctx.notify.settingsInit({
|
|
@@ -1181,32 +1658,55 @@ function registerSettingsIpc(ctx) {
|
|
|
1181
1658
|
ctx.views.hideSettings();
|
|
1182
1659
|
ctx.notify.settingsClosed();
|
|
1183
1660
|
}
|
|
1184
|
-
})
|
|
1185
|
-
|
|
1661
|
+
}).on(SettingsChannel.ConfigChanged, (_, ...args) => {
|
|
1662
|
+
const [config] = validate(
|
|
1663
|
+
SettingsChannel.ConfigChanged,
|
|
1664
|
+
SettingsConfigChangedSchema,
|
|
1665
|
+
args
|
|
1666
|
+
);
|
|
1186
1667
|
if (ctx.workspace.getProjectPath()) {
|
|
1187
|
-
ctx.workspace.saveCompileConfig(
|
|
1668
|
+
ctx.workspace.saveCompileConfig(
|
|
1669
|
+
ctx.workspace.getProjectPath(),
|
|
1670
|
+
config
|
|
1671
|
+
);
|
|
1188
1672
|
}
|
|
1189
1673
|
ctx.notify.settingsChanged(config);
|
|
1674
|
+
}).on(SettingsChannel.ProjectSettingsChanged, (_, ...args) => {
|
|
1675
|
+
const [patch] = validate(
|
|
1676
|
+
SettingsChannel.ProjectSettingsChanged,
|
|
1677
|
+
SettingsProjectSettingsChangedSchema,
|
|
1678
|
+
args
|
|
1679
|
+
);
|
|
1680
|
+
ctx.workspace.updateProjectSettings(
|
|
1681
|
+
ctx.workspace.getProjectPath(),
|
|
1682
|
+
patch
|
|
1683
|
+
);
|
|
1190
1684
|
});
|
|
1191
|
-
ipcMain7.on(
|
|
1192
|
-
SettingsChannel.ProjectSettingsChanged,
|
|
1193
|
-
(_, patch) => {
|
|
1194
|
-
ctx.workspace.updateProjectSettings(ctx.workspace.getProjectPath(), patch);
|
|
1195
|
-
}
|
|
1196
|
-
);
|
|
1197
1685
|
}
|
|
1686
|
+
var settingsModule = {
|
|
1687
|
+
setup: (ctx) => registerSettingsIpc(ctx)
|
|
1688
|
+
};
|
|
1198
1689
|
|
|
1199
1690
|
// src/main/ipc/toolbar.ts
|
|
1200
|
-
import { ipcMain as ipcMain8 } from "electron";
|
|
1201
1691
|
function registerToolbarIpc(ctx) {
|
|
1202
|
-
|
|
1692
|
+
return new IpcRegistry(ctx.senderPolicy).handle(ToolbarChannel.GetActions, async () => {
|
|
1203
1693
|
if (ctx.toolbarActions) return ctx.toolbarActions();
|
|
1204
1694
|
return [];
|
|
1205
1695
|
});
|
|
1206
1696
|
}
|
|
1207
1697
|
|
|
1698
|
+
// src/main/ipc/simulator-module.ts
|
|
1699
|
+
var simulatorModule = {
|
|
1700
|
+
setup: (ctx) => {
|
|
1701
|
+
const reg = new DisposableRegistry();
|
|
1702
|
+
reg.add(registerSimulatorIpc(ctx));
|
|
1703
|
+
reg.add(registerPanelsIpc(ctx));
|
|
1704
|
+
reg.add(registerToolbarIpc(ctx));
|
|
1705
|
+
return reg;
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1708
|
+
|
|
1208
1709
|
// src/main/services/automation/index.ts
|
|
1209
|
-
import { ipcMain as ipcMain9 } from "electron";
|
|
1210
1710
|
import { WebSocketServer } from "ws";
|
|
1211
1711
|
|
|
1212
1712
|
// src/main/services/automation/exec.ts
|
|
@@ -1595,7 +2095,6 @@ var handlers = {
|
|
|
1595
2095
|
...elementHandlers
|
|
1596
2096
|
};
|
|
1597
2097
|
var currentPort = null;
|
|
1598
|
-
var portIpcRegistered = false;
|
|
1599
2098
|
async function startAutomationServer(ctx, port = 0) {
|
|
1600
2099
|
const wss = new WebSocketServer({ port });
|
|
1601
2100
|
const clients = /* @__PURE__ */ new Set();
|
|
@@ -1606,10 +2105,10 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
1606
2105
|
const addr = wss.address();
|
|
1607
2106
|
const resolvedPort = typeof addr === "object" && addr ? addr.port : port;
|
|
1608
2107
|
currentPort = resolvedPort;
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
}
|
|
2108
|
+
const portIpc = new IpcRegistry(ctx.senderPolicy);
|
|
2109
|
+
portIpc.handle(AutomationChannel.GetPort, () => {
|
|
2110
|
+
return currentPort;
|
|
2111
|
+
});
|
|
1613
2112
|
function broadcast(event) {
|
|
1614
2113
|
const msg = JSON.stringify(event);
|
|
1615
2114
|
for (const ws of clients) {
|
|
@@ -1617,14 +2116,46 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
1617
2116
|
}
|
|
1618
2117
|
}
|
|
1619
2118
|
let consoleForwardingSetup = false;
|
|
2119
|
+
let pollInterval = null;
|
|
2120
|
+
let pollStopTimer = null;
|
|
2121
|
+
let attachedSim = null;
|
|
2122
|
+
let ipcMessageHostHandler = null;
|
|
2123
|
+
let simDestroyedHandler = null;
|
|
2124
|
+
function detachConsoleForwarding() {
|
|
2125
|
+
if (attachedSim) {
|
|
2126
|
+
if (ipcMessageHostHandler) {
|
|
2127
|
+
try {
|
|
2128
|
+
if (!attachedSim.isDestroyed()) {
|
|
2129
|
+
;
|
|
2130
|
+
attachedSim.removeListener("ipc-message-host", ipcMessageHostHandler);
|
|
2131
|
+
}
|
|
2132
|
+
} catch {
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
if (simDestroyedHandler) {
|
|
2136
|
+
try {
|
|
2137
|
+
if (!attachedSim.isDestroyed()) {
|
|
2138
|
+
attachedSim.removeListener("destroyed", simDestroyedHandler);
|
|
2139
|
+
}
|
|
2140
|
+
} catch {
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
attachedSim = null;
|
|
2145
|
+
ipcMessageHostHandler = null;
|
|
2146
|
+
simDestroyedHandler = null;
|
|
2147
|
+
}
|
|
1620
2148
|
function setupConsoleForwarding() {
|
|
1621
2149
|
if (consoleForwardingSetup) return;
|
|
1622
2150
|
consoleForwardingSetup = true;
|
|
1623
|
-
|
|
2151
|
+
pollInterval = setInterval(() => {
|
|
1624
2152
|
const sim = getSimulator(ctx);
|
|
1625
2153
|
if (!sim) return;
|
|
1626
|
-
|
|
1627
|
-
|
|
2154
|
+
if (pollInterval) {
|
|
2155
|
+
clearInterval(pollInterval);
|
|
2156
|
+
pollInterval = null;
|
|
2157
|
+
}
|
|
2158
|
+
const onIpcMessageHost = (_event, channel, data) => {
|
|
1628
2159
|
if (channel === SimulatorChannel.Console) {
|
|
1629
2160
|
const logData = data;
|
|
1630
2161
|
broadcast({
|
|
@@ -1632,9 +2163,26 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
1632
2163
|
params: { type: logData.level || "log", args: logData.args || [] }
|
|
1633
2164
|
});
|
|
1634
2165
|
}
|
|
1635
|
-
}
|
|
2166
|
+
};
|
|
2167
|
+
const onSimDestroyed = () => {
|
|
2168
|
+
attachedSim = null;
|
|
2169
|
+
ipcMessageHostHandler = null;
|
|
2170
|
+
simDestroyedHandler = null;
|
|
2171
|
+
consoleForwardingSetup = false;
|
|
2172
|
+
};
|
|
2173
|
+
attachedSim = sim;
|
|
2174
|
+
ipcMessageHostHandler = onIpcMessageHost;
|
|
2175
|
+
simDestroyedHandler = onSimDestroyed;
|
|
2176
|
+
sim.on("ipc-message-host", onIpcMessageHost);
|
|
2177
|
+
sim.once("destroyed", onSimDestroyed);
|
|
1636
2178
|
}, 1e3);
|
|
1637
|
-
setTimeout(() =>
|
|
2179
|
+
pollStopTimer = setTimeout(() => {
|
|
2180
|
+
if (pollInterval) {
|
|
2181
|
+
clearInterval(pollInterval);
|
|
2182
|
+
pollInterval = null;
|
|
2183
|
+
}
|
|
2184
|
+
pollStopTimer = null;
|
|
2185
|
+
}, 3e4);
|
|
1638
2186
|
}
|
|
1639
2187
|
wss.on("connection", (ws) => {
|
|
1640
2188
|
clients.add(ws);
|
|
@@ -1665,7 +2213,24 @@ async function startAutomationServer(ctx, port = 0) {
|
|
|
1665
2213
|
});
|
|
1666
2214
|
return {
|
|
1667
2215
|
close: () => {
|
|
2216
|
+
if (pollInterval) {
|
|
2217
|
+
clearInterval(pollInterval);
|
|
2218
|
+
pollInterval = null;
|
|
2219
|
+
}
|
|
2220
|
+
if (pollStopTimer) {
|
|
2221
|
+
clearTimeout(pollStopTimer);
|
|
2222
|
+
pollStopTimer = null;
|
|
2223
|
+
}
|
|
2224
|
+
detachConsoleForwarding();
|
|
2225
|
+
for (const ws of clients) {
|
|
2226
|
+
try {
|
|
2227
|
+
ws.close();
|
|
2228
|
+
} catch {
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
clients.clear();
|
|
1668
2232
|
if (currentPort === resolvedPort) currentPort = null;
|
|
2233
|
+
void portIpc.dispose();
|
|
1669
2234
|
wss.close();
|
|
1670
2235
|
},
|
|
1671
2236
|
port: resolvedPort
|
|
@@ -1824,7 +2389,7 @@ function getClient(kind) {
|
|
|
1824
2389
|
}
|
|
1825
2390
|
|
|
1826
2391
|
// src/main/services/mcp/tool-registry.ts
|
|
1827
|
-
import { z } from "zod";
|
|
2392
|
+
import { z as z2 } from "zod";
|
|
1828
2393
|
function registerCommonTargetTools(server, kind) {
|
|
1829
2394
|
const descriptions = kind === "simulator" ? {
|
|
1830
2395
|
screenshot: "Take a screenshot of the simulator webview",
|
|
@@ -1845,9 +2410,9 @@ function registerCommonTargetTools(server, kind) {
|
|
|
1845
2410
|
return { content: [{ type: "image", data, mimeType: "image/png" }] };
|
|
1846
2411
|
});
|
|
1847
2412
|
server.tool(`${kind}_console_logs`, descriptions.consoleLogs, {
|
|
1848
|
-
limit:
|
|
1849
|
-
level:
|
|
1850
|
-
sinceTimestamp:
|
|
2413
|
+
limit: z2.number().optional().default(50).describe("Maximum number of log entries"),
|
|
2414
|
+
level: z2.enum(["error", "warning", "warn", "info", "log", "debug"]).optional().describe("Only return entries with this level"),
|
|
2415
|
+
sinceTimestamp: z2.string().optional().describe("ISO timestamp; only return entries at or after this time")
|
|
1851
2416
|
}, async ({ limit, level, sinceTimestamp }) => {
|
|
1852
2417
|
getClient(kind);
|
|
1853
2418
|
let entries = getTargetState(kind).consoleLogs;
|
|
@@ -1862,7 +2427,7 @@ function registerCommonTargetTools(server, kind) {
|
|
|
1862
2427
|
});
|
|
1863
2428
|
if (kind !== "workbench") {
|
|
1864
2429
|
server.tool(`${kind}_evaluate`, descriptions.evaluate, {
|
|
1865
|
-
expression:
|
|
2430
|
+
expression: z2.string().describe("JavaScript expression to evaluate")
|
|
1866
2431
|
}, async ({ expression }) => {
|
|
1867
2432
|
const c = getClient(kind);
|
|
1868
2433
|
const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
|
|
@@ -1875,15 +2440,15 @@ ${JSON.stringify(result.exceptionDetails, null, 2)}` }], isError: true };
|
|
|
1875
2440
|
});
|
|
1876
2441
|
}
|
|
1877
2442
|
server.tool(`${kind}_get_dom`, descriptions.getDom, {
|
|
1878
|
-
depth:
|
|
2443
|
+
depth: z2.number().optional().default(3).describe("Maximum depth")
|
|
1879
2444
|
}, async ({ depth }) => {
|
|
1880
2445
|
const c = getClient(kind);
|
|
1881
2446
|
const { root } = await c.DOM.getDocument({ depth });
|
|
1882
2447
|
return { content: [{ type: "text", text: JSON.stringify(root, null, 2) }] };
|
|
1883
2448
|
});
|
|
1884
2449
|
server.tool(`${kind}_network_log`, descriptions.networkLog, {
|
|
1885
|
-
limit:
|
|
1886
|
-
minStatus:
|
|
2450
|
+
limit: z2.number().optional().default(20).describe("Maximum number of entries"),
|
|
2451
|
+
minStatus: z2.number().optional().describe("Only return entries with status >= minStatus (status 0 = failed before response is included when minStatus >= 400)")
|
|
1887
2452
|
}, async ({ limit, minStatus }) => {
|
|
1888
2453
|
getClient(kind);
|
|
1889
2454
|
let entries = getTargetState(kind).networkRequests;
|
|
@@ -2126,11 +2691,11 @@ function registerContextTools(server) {
|
|
|
2126
2691
|
}
|
|
2127
2692
|
|
|
2128
2693
|
// src/main/services/mcp/tools/simulator-tools.ts
|
|
2129
|
-
import { z as
|
|
2694
|
+
import { z as z3 } from "zod";
|
|
2130
2695
|
function registerSimulatorTools(server) {
|
|
2131
2696
|
server.tool("simulator_navigate", "Navigate the simulator to a URL, or reload the current page", {
|
|
2132
|
-
url:
|
|
2133
|
-
reload:
|
|
2697
|
+
url: z3.string().optional().describe("URL to navigate to"),
|
|
2698
|
+
reload: z3.boolean().optional().describe("If true, reload current page instead of navigating")
|
|
2134
2699
|
}, async ({ url, reload }) => {
|
|
2135
2700
|
const c = getClient("simulator");
|
|
2136
2701
|
if (reload) {
|
|
@@ -2145,15 +2710,15 @@ function registerSimulatorTools(server) {
|
|
|
2145
2710
|
return { content: [{ type: "text", text: "Error: either url or reload is required" }], isError: true };
|
|
2146
2711
|
});
|
|
2147
2712
|
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:
|
|
2713
|
+
action: z3.enum(["tap_coord", "tap_selector", "type", "scroll", "key"]).describe("Input action to dispatch"),
|
|
2714
|
+
x: z3.number().optional().describe("Viewport x coordinate for `tap_coord` or `scroll`"),
|
|
2715
|
+
y: z3.number().optional().describe("Viewport y coordinate for `tap_coord` or `scroll`"),
|
|
2716
|
+
selector: z3.string().optional().describe("CSS selector for `tap_selector` or `type`"),
|
|
2717
|
+
nth: z3.number().int().optional().describe("Zero-based index when selector matches multiple elements; defaults to 0"),
|
|
2718
|
+
text: z3.string().optional().describe("Text to insert for `type`"),
|
|
2719
|
+
key: z3.string().optional().describe("Best-effort keyboard key name for `key`; common keys such as Enter, Escape, Tab, Arrow* and Space work best"),
|
|
2720
|
+
deltaX: z3.number().optional().describe("Horizontal wheel delta for `scroll`"),
|
|
2721
|
+
deltaY: z3.number().optional().describe("Vertical wheel delta for `scroll`")
|
|
2157
2722
|
}, async ({ action, x, y, selector, nth, text, key, deltaX, deltaY }) => {
|
|
2158
2723
|
const c = getClient("simulator");
|
|
2159
2724
|
const err = (msg) => ({ content: [{ type: "text", text: `Error: ${msg}` }], isError: true });
|
|
@@ -2308,87 +2873,293 @@ function startMcpServer(resolvedCdpPort, mcpPort) {
|
|
|
2308
2873
|
httpServer.listen(mcpPort, "127.0.0.1", () => {
|
|
2309
2874
|
console.log(`[MCP] SSE server listening on http://127.0.0.1:${mcpPort}/sse`);
|
|
2310
2875
|
});
|
|
2876
|
+
return toDisposable(() => {
|
|
2877
|
+
transports.clear();
|
|
2878
|
+
httpServer.close();
|
|
2879
|
+
});
|
|
2311
2880
|
}
|
|
2312
2881
|
|
|
2313
2882
|
// src/main/services/simulator-storage/index.ts
|
|
2314
|
-
import { app as app8,
|
|
2315
|
-
var attachedWc = null;
|
|
2316
|
-
var hostWc = null;
|
|
2883
|
+
import { app as app8, webContents as wcStatic } from "electron";
|
|
2317
2884
|
function isSimulatorWebview(wc) {
|
|
2318
2885
|
if (wc.isDestroyed()) return false;
|
|
2319
2886
|
if (wc.getType() !== "webview") return false;
|
|
2320
2887
|
return wc.getURL().includes("simulator.html");
|
|
2321
2888
|
}
|
|
2322
|
-
function
|
|
2323
|
-
if (!attachedWc) return;
|
|
2324
|
-
const wc = attachedWc;
|
|
2325
|
-
attachedWc = null;
|
|
2889
|
+
function safeOff(target, event, fn) {
|
|
2326
2890
|
try {
|
|
2327
|
-
if (
|
|
2328
|
-
|
|
2329
|
-
}
|
|
2891
|
+
if (target.isDestroyed?.()) return;
|
|
2892
|
+
target.removeListener(event, fn);
|
|
2330
2893
|
} catch {
|
|
2331
2894
|
}
|
|
2332
2895
|
}
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2896
|
+
function setupSimulatorStorage(host, options) {
|
|
2897
|
+
const registry = new DisposableRegistry();
|
|
2898
|
+
const getActiveAppId = options.getActiveAppId;
|
|
2899
|
+
let attachedWc = null;
|
|
2900
|
+
let attachDisposables = null;
|
|
2901
|
+
let cachedOrigin = null;
|
|
2902
|
+
function activePrefix() {
|
|
2903
|
+
const appId = getActiveAppId();
|
|
2904
|
+
return appId ? `${appId}_` : null;
|
|
2905
|
+
}
|
|
2906
|
+
function detachFromSim() {
|
|
2907
|
+
if (!attachedWc) return;
|
|
2908
|
+
const wc = attachedWc;
|
|
2909
|
+
attachedWc = null;
|
|
2910
|
+
cachedOrigin = null;
|
|
2911
|
+
const ad = attachDisposables;
|
|
2912
|
+
attachDisposables = null;
|
|
2913
|
+
if (ad) void ad.disposeAll().catch(() => {
|
|
2345
2914
|
});
|
|
2346
|
-
|
|
2347
|
-
if (
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2915
|
+
try {
|
|
2916
|
+
if (!wc.isDestroyed() && wc.debugger.isAttached()) {
|
|
2917
|
+
wc.debugger.detach();
|
|
2918
|
+
}
|
|
2919
|
+
} catch {
|
|
2920
|
+
}
|
|
2352
2921
|
}
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2922
|
+
async function attachToSim(wc) {
|
|
2923
|
+
if (attachedWc === wc) return;
|
|
2924
|
+
if (wc.isDestroyed()) return;
|
|
2925
|
+
detachFromSim();
|
|
2926
|
+
try {
|
|
2927
|
+
if (!wc.debugger.isAttached()) {
|
|
2928
|
+
wc.debugger.attach("1.3");
|
|
2929
|
+
}
|
|
2930
|
+
await wc.debugger.sendCommand("DOMStorage.enable");
|
|
2931
|
+
const attach = new DisposableRegistry();
|
|
2932
|
+
attachDisposables = attach;
|
|
2933
|
+
const onMessage = (_event, method, params) => forwardCdpMessage(method, params);
|
|
2934
|
+
wc.debugger.on("message", onMessage);
|
|
2935
|
+
attach.add(() => safeOff(wc.debugger, "message", onMessage));
|
|
2936
|
+
const onDetach = () => {
|
|
2937
|
+
if (attachedWc === wc) attachedWc = null;
|
|
2368
2938
|
};
|
|
2939
|
+
wc.debugger.on("detach", onDetach);
|
|
2940
|
+
attach.add(() => safeOff(wc.debugger, "detach", onDetach));
|
|
2941
|
+
const onDestroyed = () => {
|
|
2942
|
+
if (attachedWc === wc) {
|
|
2943
|
+
attachedWc = null;
|
|
2944
|
+
const ad = attachDisposables;
|
|
2945
|
+
attachDisposables = null;
|
|
2946
|
+
if (ad) void ad.disposeAll().catch(() => {
|
|
2947
|
+
});
|
|
2948
|
+
}
|
|
2949
|
+
};
|
|
2950
|
+
wc.once("destroyed", onDestroyed);
|
|
2951
|
+
attach.add(() => safeOff(wc, "destroyed", onDestroyed));
|
|
2952
|
+
attachedWc = wc;
|
|
2953
|
+
} catch (e) {
|
|
2954
|
+
console.warn("[storage-watcher] attach failed:", e.message);
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
function forwardCdpMessage(method, params) {
|
|
2958
|
+
if (host.isDestroyed()) return;
|
|
2959
|
+
const p = params;
|
|
2960
|
+
const prefix = activePrefix();
|
|
2961
|
+
let evt;
|
|
2962
|
+
switch (method) {
|
|
2963
|
+
case "DOMStorage.domStorageItemAdded": {
|
|
2964
|
+
const key = String(p.key);
|
|
2965
|
+
if (prefix && !key.startsWith(prefix)) return;
|
|
2966
|
+
evt = { type: "added", key, newValue: String(p.newValue ?? "") };
|
|
2967
|
+
break;
|
|
2968
|
+
}
|
|
2969
|
+
case "DOMStorage.domStorageItemUpdated": {
|
|
2970
|
+
const key = String(p.key);
|
|
2971
|
+
if (prefix && !key.startsWith(prefix)) return;
|
|
2972
|
+
evt = {
|
|
2973
|
+
type: "updated",
|
|
2974
|
+
key,
|
|
2975
|
+
oldValue: String(p.oldValue ?? ""),
|
|
2976
|
+
newValue: String(p.newValue ?? "")
|
|
2977
|
+
};
|
|
2978
|
+
break;
|
|
2979
|
+
}
|
|
2980
|
+
case "DOMStorage.domStorageItemRemoved": {
|
|
2981
|
+
const key = String(p.key);
|
|
2982
|
+
if (prefix && !key.startsWith(prefix)) return;
|
|
2983
|
+
evt = { type: "removed", key };
|
|
2984
|
+
break;
|
|
2985
|
+
}
|
|
2986
|
+
case "DOMStorage.domStorageItemsCleared":
|
|
2987
|
+
evt = { type: "cleared" };
|
|
2988
|
+
break;
|
|
2989
|
+
default:
|
|
2990
|
+
return;
|
|
2991
|
+
}
|
|
2992
|
+
host.send(SimulatorStorageChannel.Event, evt);
|
|
2993
|
+
}
|
|
2994
|
+
async function ensureAttached() {
|
|
2995
|
+
if (attachedWc && !attachedWc.isDestroyed()) return;
|
|
2996
|
+
const sim = findSimulatorWebContents();
|
|
2997
|
+
if (!sim) return;
|
|
2998
|
+
await attachToSim(sim);
|
|
2999
|
+
}
|
|
3000
|
+
async function getStorageId() {
|
|
3001
|
+
if (!attachedWc || attachedWc.isDestroyed()) return null;
|
|
3002
|
+
if (!cachedOrigin) {
|
|
3003
|
+
try {
|
|
3004
|
+
cachedOrigin = String(await attachedWc.executeJavaScript("location.origin"));
|
|
3005
|
+
} catch {
|
|
3006
|
+
return null;
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
return { securityOrigin: cachedOrigin, isLocalStorage: true };
|
|
3010
|
+
}
|
|
3011
|
+
function pushSyntheticEvent(evt) {
|
|
3012
|
+
if (host.isDestroyed()) return;
|
|
3013
|
+
const prefix = activePrefix();
|
|
3014
|
+
if (prefix && !evt.key.startsWith(prefix)) return;
|
|
3015
|
+
host.send(SimulatorStorageChannel.Event, evt);
|
|
3016
|
+
}
|
|
3017
|
+
async function setItem(key, value) {
|
|
3018
|
+
await ensureAttached();
|
|
3019
|
+
if (!attachedWc || attachedWc.isDestroyed()) {
|
|
3020
|
+
return { ok: false, error: "simulator not attached" };
|
|
3021
|
+
}
|
|
3022
|
+
const storageId = await getStorageId();
|
|
3023
|
+
if (!storageId) return { ok: false, error: "failed to resolve simulator origin" };
|
|
3024
|
+
try {
|
|
3025
|
+
await attachedWc.debugger.sendCommand("DOMStorage.setDOMStorageItem", {
|
|
3026
|
+
storageId,
|
|
3027
|
+
key,
|
|
3028
|
+
value
|
|
3029
|
+
});
|
|
3030
|
+
pushSyntheticEvent({ type: "added", key, newValue: value });
|
|
3031
|
+
return { ok: true };
|
|
3032
|
+
} catch (e) {
|
|
3033
|
+
return { ok: false, error: e.message };
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
async function removeItem(key) {
|
|
3037
|
+
await ensureAttached();
|
|
3038
|
+
if (!attachedWc || attachedWc.isDestroyed()) {
|
|
3039
|
+
return { ok: false, error: "simulator not attached" };
|
|
3040
|
+
}
|
|
3041
|
+
const storageId = await getStorageId();
|
|
3042
|
+
if (!storageId) return { ok: false, error: "failed to resolve simulator origin" };
|
|
3043
|
+
try {
|
|
3044
|
+
await attachedWc.debugger.sendCommand("DOMStorage.removeDOMStorageItem", {
|
|
3045
|
+
storageId,
|
|
3046
|
+
key
|
|
3047
|
+
});
|
|
3048
|
+
pushSyntheticEvent({ type: "removed", key });
|
|
3049
|
+
return { ok: true };
|
|
3050
|
+
} catch (e) {
|
|
3051
|
+
return { ok: false, error: e.message };
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
async function clearScoped() {
|
|
3055
|
+
await ensureAttached();
|
|
3056
|
+
if (!attachedWc || attachedWc.isDestroyed()) {
|
|
3057
|
+
return { ok: false, error: "simulator not attached" };
|
|
3058
|
+
}
|
|
3059
|
+
const storageId = await getStorageId();
|
|
3060
|
+
if (!storageId) return { ok: false, error: "failed to resolve simulator origin" };
|
|
3061
|
+
const prefix = activePrefix();
|
|
3062
|
+
try {
|
|
3063
|
+
if (!prefix) {
|
|
3064
|
+
await attachedWc.debugger.sendCommand("DOMStorage.clear", { storageId });
|
|
3065
|
+
return { ok: true };
|
|
3066
|
+
}
|
|
3067
|
+
const result = await attachedWc.debugger.sendCommand("DOMStorage.getDOMStorageItems", {
|
|
3068
|
+
storageId
|
|
3069
|
+
});
|
|
3070
|
+
for (const [key] of result.entries) {
|
|
3071
|
+
if (!key.startsWith(prefix)) continue;
|
|
3072
|
+
await attachedWc.debugger.sendCommand("DOMStorage.removeDOMStorageItem", {
|
|
3073
|
+
storageId,
|
|
3074
|
+
key
|
|
3075
|
+
});
|
|
3076
|
+
}
|
|
3077
|
+
return { ok: true };
|
|
3078
|
+
} catch (e) {
|
|
3079
|
+
return { ok: false, error: e.message };
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
async function clearAll() {
|
|
3083
|
+
await ensureAttached();
|
|
3084
|
+
if (!attachedWc || attachedWc.isDestroyed()) {
|
|
3085
|
+
return { ok: false, error: "simulator not attached" };
|
|
3086
|
+
}
|
|
3087
|
+
const storageId = await getStorageId();
|
|
3088
|
+
if (!storageId) return { ok: false, error: "failed to resolve simulator origin" };
|
|
3089
|
+
try {
|
|
3090
|
+
await attachedWc.debugger.sendCommand("DOMStorage.clear", { storageId });
|
|
3091
|
+
return { ok: true };
|
|
3092
|
+
} catch (e) {
|
|
3093
|
+
return { ok: false, error: e.message };
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
async function getSnapshot() {
|
|
3097
|
+
await ensureAttached();
|
|
3098
|
+
if (!attachedWc || attachedWc.isDestroyed()) return [];
|
|
3099
|
+
try {
|
|
3100
|
+
const storageId = await getStorageId();
|
|
3101
|
+
if (!storageId) return [];
|
|
3102
|
+
const result = await attachedWc.debugger.sendCommand("DOMStorage.getDOMStorageItems", {
|
|
3103
|
+
storageId
|
|
3104
|
+
});
|
|
3105
|
+
const prefix = activePrefix();
|
|
3106
|
+
const filtered = prefix ? result.entries.filter(([key]) => key.startsWith(prefix)) : result.entries;
|
|
3107
|
+
return filtered.map(([key, value]) => ({ key, value }));
|
|
3108
|
+
} catch {
|
|
3109
|
+
return [];
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
for (const wc of wcStatic.getAllWebContents()) {
|
|
3113
|
+
if (isSimulatorWebview(wc)) {
|
|
3114
|
+
void attachToSim(wc);
|
|
2369
3115
|
break;
|
|
2370
|
-
|
|
2371
|
-
evt = { type: "removed", key: String(p.key) };
|
|
2372
|
-
break;
|
|
2373
|
-
case "DOMStorage.domStorageItemsCleared":
|
|
2374
|
-
evt = { type: "cleared" };
|
|
2375
|
-
break;
|
|
2376
|
-
default:
|
|
2377
|
-
return;
|
|
3116
|
+
}
|
|
2378
3117
|
}
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
3118
|
+
const wcSubs = /* @__PURE__ */ new Map();
|
|
3119
|
+
const onWcCreated = (_event, wc) => {
|
|
3120
|
+
const sub = new DisposableRegistry();
|
|
3121
|
+
wcSubs.set(wc, sub);
|
|
3122
|
+
const onFinishLoad = () => {
|
|
3123
|
+
if (isSimulatorWebview(wc)) void attachToSim(wc);
|
|
3124
|
+
};
|
|
3125
|
+
wc.on("did-finish-load", onFinishLoad);
|
|
3126
|
+
sub.add(() => safeOff(wc, "did-finish-load", onFinishLoad));
|
|
3127
|
+
wc.once("destroyed", () => {
|
|
3128
|
+
void sub.disposeAll().catch(() => {
|
|
3129
|
+
});
|
|
3130
|
+
wcSubs.delete(wc);
|
|
2387
3131
|
});
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
3132
|
+
};
|
|
3133
|
+
app8.on("web-contents-created", onWcCreated);
|
|
3134
|
+
registry.add(() => {
|
|
3135
|
+
app8.removeListener("web-contents-created", onWcCreated);
|
|
3136
|
+
});
|
|
3137
|
+
registry.add(async () => {
|
|
3138
|
+
const subs = Array.from(wcSubs.values());
|
|
3139
|
+
wcSubs.clear();
|
|
3140
|
+
for (const sub of subs) {
|
|
3141
|
+
await sub.disposeAll().catch(() => {
|
|
3142
|
+
});
|
|
3143
|
+
}
|
|
3144
|
+
});
|
|
3145
|
+
const ipc = new IpcRegistry(options.senderPolicy);
|
|
3146
|
+
ipc.handle(SimulatorStorageChannel.GetSnapshot, () => getSnapshot());
|
|
3147
|
+
ipc.handle(SimulatorStorageChannel.GetActivePrefix, () => activePrefix() ?? "");
|
|
3148
|
+
ipc.handle(
|
|
3149
|
+
SimulatorStorageChannel.Set,
|
|
3150
|
+
(_event, payload) => setItem(String(payload?.key ?? ""), String(payload?.value ?? ""))
|
|
3151
|
+
);
|
|
3152
|
+
ipc.handle(
|
|
3153
|
+
SimulatorStorageChannel.Remove,
|
|
3154
|
+
(_event, payload) => removeItem(String(payload?.key ?? ""))
|
|
3155
|
+
);
|
|
3156
|
+
ipc.handle(SimulatorStorageChannel.Clear, () => clearScoped());
|
|
3157
|
+
ipc.handle(SimulatorStorageChannel.ClearAll, () => clearAll());
|
|
3158
|
+
ipc.handle(SimulatorElementChannel.Inspect, (_event, sid) => inspectElement(sid));
|
|
3159
|
+
ipc.handle(SimulatorElementChannel.Clear, () => clearElementInspection());
|
|
3160
|
+
registry.add(ipc);
|
|
3161
|
+
registry.add(() => detachFromSim());
|
|
3162
|
+
return registry;
|
|
2392
3163
|
}
|
|
2393
3164
|
function findSimulatorWebContents() {
|
|
2394
3165
|
for (const wc of wcStatic.getAllWebContents()) {
|
|
@@ -2420,30 +3191,9 @@ async function clearElementInspection() {
|
|
|
2420
3191
|
} catch {
|
|
2421
3192
|
}
|
|
2422
3193
|
}
|
|
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
3194
|
|
|
2445
3195
|
// src/main/services/update/update-manager.ts
|
|
2446
|
-
import { app as app9,
|
|
3196
|
+
import { app as app9, shell as shell3 } from "electron";
|
|
2447
3197
|
var UpdateManager = class {
|
|
2448
3198
|
checker;
|
|
2449
3199
|
mainWindow;
|
|
@@ -2452,23 +3202,17 @@ var UpdateManager = class {
|
|
|
2452
3202
|
downloadedPath = null;
|
|
2453
3203
|
timer = null;
|
|
2454
3204
|
initialTimer = null;
|
|
3205
|
+
ipc;
|
|
2455
3206
|
constructor(opts) {
|
|
2456
3207
|
this.checker = opts.checker;
|
|
2457
3208
|
this.mainWindow = opts.mainWindow;
|
|
2458
3209
|
this.getCurrentVersion = opts.getCurrentVersion ?? (() => app9.getVersion());
|
|
3210
|
+
this.ipc = new IpcRegistry(opts.senderPolicy);
|
|
2459
3211
|
this.registerIpc();
|
|
2460
3212
|
this.startPeriodicCheck(opts.checkInterval ?? 60 * 60 * 1e3, opts.initialDelay ?? 5e3);
|
|
2461
3213
|
}
|
|
2462
3214
|
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
|
-
});
|
|
3215
|
+
this.ipc.handle(UpdateChannel.Check, async () => this.check()).handle(UpdateChannel.Download, async () => this.download()).handle(UpdateChannel.Install, async () => this.install());
|
|
2472
3216
|
}
|
|
2473
3217
|
async check() {
|
|
2474
3218
|
try {
|
|
@@ -2491,7 +3235,7 @@ var UpdateManager = class {
|
|
|
2491
3235
|
}
|
|
2492
3236
|
try {
|
|
2493
3237
|
const filePath = await this.checker.downloadUpdate(this.latestUpdate, (percent) => {
|
|
2494
|
-
this.mainWindow.webContents.send(
|
|
3238
|
+
this.mainWindow.webContents.send(UpdateChannel.DownloadProgress, { percent });
|
|
2495
3239
|
});
|
|
2496
3240
|
this.downloadedPath = filePath;
|
|
2497
3241
|
return { success: true, filePath };
|
|
@@ -2502,10 +3246,16 @@ var UpdateManager = class {
|
|
|
2502
3246
|
}
|
|
2503
3247
|
install() {
|
|
2504
3248
|
if (this.downloadedPath) {
|
|
2505
|
-
|
|
3249
|
+
shell3.openPath(this.downloadedPath);
|
|
2506
3250
|
app9.quit();
|
|
2507
3251
|
}
|
|
2508
3252
|
}
|
|
3253
|
+
/**
|
|
3254
|
+
* Tear down timers + IPC handlers. Returns a Promise so callers that need
|
|
3255
|
+
* a settled cleanup (e.g. workbench registry shutdown, the unit-test
|
|
3256
|
+
* lifecycle suite) can await it; calling without await is also safe since
|
|
3257
|
+
* the timer clears + handler removals are scheduled synchronously.
|
|
3258
|
+
*/
|
|
2509
3259
|
dispose() {
|
|
2510
3260
|
if (this.timer) {
|
|
2511
3261
|
clearInterval(this.timer);
|
|
@@ -2515,9 +3265,7 @@ var UpdateManager = class {
|
|
|
2515
3265
|
clearTimeout(this.initialTimer);
|
|
2516
3266
|
this.initialTimer = null;
|
|
2517
3267
|
}
|
|
2518
|
-
|
|
2519
|
-
ipcMain11.removeHandler("updates:download");
|
|
2520
|
-
ipcMain11.removeHandler("updates:install");
|
|
3268
|
+
return this.ipc.dispose();
|
|
2521
3269
|
}
|
|
2522
3270
|
startPeriodicCheck(interval, initialDelay) {
|
|
2523
3271
|
this.initialTimer = setTimeout(() => void this.checkAndNotify(), initialDelay);
|
|
@@ -2526,7 +3274,7 @@ var UpdateManager = class {
|
|
|
2526
3274
|
async checkAndNotify() {
|
|
2527
3275
|
const result = await this.check();
|
|
2528
3276
|
if (result.hasUpdate && result.info) {
|
|
2529
|
-
this.mainWindow.webContents.send(
|
|
3277
|
+
this.mainWindow.webContents.send(UpdateChannel.Available, result.info);
|
|
2530
3278
|
}
|
|
2531
3279
|
}
|
|
2532
3280
|
};
|
|
@@ -2542,16 +3290,12 @@ var DEFAULT_MODULES = {
|
|
|
2542
3290
|
popover: true,
|
|
2543
3291
|
settings: true
|
|
2544
3292
|
};
|
|
2545
|
-
var
|
|
2546
|
-
projects:
|
|
2547
|
-
session:
|
|
2548
|
-
simulator:
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
registerToolbarIpc(ctx);
|
|
2552
|
-
},
|
|
2553
|
-
popover: registerPopoverIpc,
|
|
2554
|
-
settings: registerSettingsIpc
|
|
3293
|
+
var BUILTIN_MODULES = {
|
|
3294
|
+
projects: projectsModule,
|
|
3295
|
+
session: sessionModule,
|
|
3296
|
+
simulator: simulatorModule,
|
|
3297
|
+
popover: popoverModule,
|
|
3298
|
+
settings: settingsModule
|
|
2555
3299
|
};
|
|
2556
3300
|
function parseAutoArgs() {
|
|
2557
3301
|
const argv = process.argv;
|
|
@@ -2576,18 +3320,25 @@ function resolveModules(config) {
|
|
|
2576
3320
|
...config.modules
|
|
2577
3321
|
};
|
|
2578
3322
|
}
|
|
2579
|
-
async function disposeContext(ctx
|
|
2580
|
-
updateManager?.dispose();
|
|
3323
|
+
async function disposeContext(ctx) {
|
|
2581
3324
|
await ctx.workspace.closeProject();
|
|
3325
|
+
await ctx.registry.dispose().catch((err) => {
|
|
3326
|
+
console.warn("[workbench] dispose registry encountered errors:", err);
|
|
3327
|
+
});
|
|
2582
3328
|
}
|
|
2583
3329
|
function createConfiguredMainWindow(config, rendererDir2) {
|
|
2584
3330
|
const mainWindow = createMainWindow({
|
|
2585
3331
|
title: config.appName ?? "Dimina DevTools",
|
|
2586
|
-
indexHtml:
|
|
3332
|
+
indexHtml: path8.join(rendererDir2, "entries/main/index.html"),
|
|
2587
3333
|
width: config.window?.width,
|
|
2588
3334
|
height: config.window?.height,
|
|
2589
3335
|
minWidth: config.window?.minWidth,
|
|
2590
|
-
minHeight: config.window?.minHeight
|
|
3336
|
+
minHeight: config.window?.minHeight,
|
|
3337
|
+
// P1: pass the same preload path the workbench context stores on
|
|
3338
|
+
// ctx.preloadPath so hosts that ship a custom simulator preload are the
|
|
3339
|
+
// ones registered on the persist:simulator session — otherwise the
|
|
3340
|
+
// backstop wouldn't be the host's script.
|
|
3341
|
+
simulatorPreloadPath: config.preloadPath ?? defaultPreloadPath
|
|
2591
3342
|
});
|
|
2592
3343
|
if (config.icon) {
|
|
2593
3344
|
const icon = nativeImage.createFromPath(config.icon);
|
|
@@ -2611,7 +3362,7 @@ function createContext(config, mainWindow, rendererDir2) {
|
|
|
2611
3362
|
function registerBuiltinModules(config, context) {
|
|
2612
3363
|
const modules = resolveModules(config);
|
|
2613
3364
|
Object.keys(modules).forEach((moduleId) => {
|
|
2614
|
-
if (modules[moduleId])
|
|
3365
|
+
if (modules[moduleId]) context.registry.add(BUILTIN_MODULES[moduleId].setup(context));
|
|
2615
3366
|
});
|
|
2616
3367
|
}
|
|
2617
3368
|
function installMenu(config, mainWindow, context) {
|
|
@@ -2626,19 +3377,20 @@ async function setupAutomation(instance) {
|
|
|
2626
3377
|
if (autoArgs.auto) {
|
|
2627
3378
|
const server = await startAutomationServer(instance.context, autoArgs.autoPort);
|
|
2628
3379
|
instance.automationServer = server;
|
|
3380
|
+
instance.context.registry.add(() => server.close());
|
|
2629
3381
|
console.log(`[automation] listening on ws://127.0.0.1:${server.port}`);
|
|
2630
3382
|
}
|
|
2631
3383
|
}
|
|
2632
3384
|
function setupMcp() {
|
|
2633
3385
|
const settings = loadWorkbenchSettings();
|
|
2634
|
-
if (!settings.mcp.enabled) return;
|
|
3386
|
+
if (!settings.mcp.enabled) return null;
|
|
2635
3387
|
const cdpPortSwitch = app11.commandLine.getSwitchValue("remote-debugging-port");
|
|
2636
3388
|
const cdpPort2 = cdpPortSwitch ? parseInt(cdpPortSwitch, 10) : settings.cdp.port;
|
|
2637
|
-
startMcpServer(cdpPort2, settings.mcp.port);
|
|
3389
|
+
return startMcpServer(cdpPort2, settings.mcp.port);
|
|
2638
3390
|
}
|
|
2639
3391
|
function wireAppWindowEvents(config, instance) {
|
|
2640
3392
|
const { mainWindow, context } = instance;
|
|
2641
|
-
wireMainWindowEvents(mainWindow, {
|
|
3393
|
+
return wireMainWindowEvents(mainWindow, {
|
|
2642
3394
|
context,
|
|
2643
3395
|
onResize: () => context.views.repositionAll(),
|
|
2644
3396
|
onClose: async (e) => {
|
|
@@ -2647,23 +3399,32 @@ function wireAppWindowEvents(config, instance) {
|
|
|
2647
3399
|
if (config.onBeforeClose) {
|
|
2648
3400
|
await config.onBeforeClose(instance);
|
|
2649
3401
|
}
|
|
2650
|
-
await
|
|
3402
|
+
await context.workspace.closeProject();
|
|
2651
3403
|
context.notify.windowNavigateBack();
|
|
2652
3404
|
}
|
|
2653
3405
|
});
|
|
2654
3406
|
}
|
|
2655
3407
|
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);
|
|
3408
|
+
if (app11.isPackaged) {
|
|
3409
|
+
return toDisposable(() => {
|
|
2665
3410
|
});
|
|
2666
3411
|
}
|
|
3412
|
+
let reloadTimer = null;
|
|
3413
|
+
const watcher = fs5.watch(rendererDir2, { recursive: true }, () => {
|
|
3414
|
+
if (reloadTimer) clearTimeout(reloadTimer);
|
|
3415
|
+
reloadTimer = setTimeout(() => {
|
|
3416
|
+
for (const win of BrowserWindow3.getAllWindows()) {
|
|
3417
|
+
win.webContents.reload();
|
|
3418
|
+
}
|
|
3419
|
+
}, 300);
|
|
3420
|
+
});
|
|
3421
|
+
return toDisposable(() => {
|
|
3422
|
+
if (reloadTimer) {
|
|
3423
|
+
clearTimeout(reloadTimer);
|
|
3424
|
+
reloadTimer = null;
|
|
3425
|
+
}
|
|
3426
|
+
watcher.close();
|
|
3427
|
+
});
|
|
2667
3428
|
}
|
|
2668
3429
|
function createWorkbenchApp(config = {}) {
|
|
2669
3430
|
let setupPromise = null;
|
|
@@ -2676,13 +3437,13 @@ function createWorkbenchApp(config = {}) {
|
|
|
2676
3437
|
const rendererDir2 = config.rendererDir ?? rendererDir;
|
|
2677
3438
|
const mainWindow = createConfiguredMainWindow(config, rendererDir2);
|
|
2678
3439
|
const context = createContext(config, mainWindow, rendererDir2);
|
|
2679
|
-
registerAppIpc(context);
|
|
3440
|
+
context.registry.add(registerAppIpc(context));
|
|
2680
3441
|
registerBuiltinModules(config, context);
|
|
2681
3442
|
installMenu(config, mainWindow, context);
|
|
2682
3443
|
const instance = {
|
|
2683
3444
|
mainWindow,
|
|
2684
3445
|
context,
|
|
2685
|
-
dispose: () => disposeContext(context
|
|
3446
|
+
dispose: () => disposeContext(context)
|
|
2686
3447
|
};
|
|
2687
3448
|
if (config.onSetup) {
|
|
2688
3449
|
await config.onSetup(instance);
|
|
@@ -2695,22 +3456,37 @@ function createWorkbenchApp(config = {}) {
|
|
|
2695
3456
|
initialDelay: config.updateOptions?.initialDelay,
|
|
2696
3457
|
getCurrentVersion: config.updateOptions?.getCurrentVersion
|
|
2697
3458
|
});
|
|
3459
|
+
context.registry.add(() => instance.updateManager.dispose());
|
|
2698
3460
|
}
|
|
2699
3461
|
await setupAutomation(instance);
|
|
2700
|
-
setupMcp();
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
3462
|
+
const mcp = setupMcp();
|
|
3463
|
+
if (mcp) context.registry.add(mcp);
|
|
3464
|
+
context.registry.add(setupSimulatorStorage(mainWindow.webContents, {
|
|
3465
|
+
senderPolicy: context.senderPolicy,
|
|
3466
|
+
// Per-project filter for the simulator-storage panel: the simulator
|
|
3467
|
+
// uses a fixed `persist:simulator` partition + a fixed simulator.html
|
|
3468
|
+
// origin, so localStorage is shared across every project that has
|
|
3469
|
+
// ever opened. The dimina runtime isolates writes with `${appId}_`
|
|
3470
|
+
// prefixes; this callback lets the storage panel filter the CDP
|
|
3471
|
+
// snapshot/event stream to the active appId.
|
|
3472
|
+
getActiveAppId: () => {
|
|
3473
|
+
const session2 = context.workspace.getSession();
|
|
3474
|
+
const appInfo = session2?.appInfo;
|
|
3475
|
+
return appInfo?.appId ?? null;
|
|
3476
|
+
}
|
|
3477
|
+
}));
|
|
3478
|
+
context.registry.add(wireAppWindowEvents(config, instance));
|
|
3479
|
+
context.registry.add(enableDevRendererAutoReload(rendererDir2));
|
|
2704
3480
|
return instance;
|
|
2705
3481
|
});
|
|
2706
3482
|
return setupPromise;
|
|
2707
3483
|
}
|
|
2708
3484
|
function start() {
|
|
2709
|
-
void setup();
|
|
2710
3485
|
if (!appEventsRegistered) {
|
|
2711
3486
|
appEventsRegistered = true;
|
|
2712
3487
|
registerAppLifecycle();
|
|
2713
3488
|
}
|
|
3489
|
+
return setup().then(() => void 0);
|
|
2714
3490
|
}
|
|
2715
3491
|
return {
|
|
2716
3492
|
setup,
|
|
@@ -2720,7 +3496,7 @@ function createWorkbenchApp(config = {}) {
|
|
|
2720
3496
|
|
|
2721
3497
|
// src/main/windows/settings-window/create.ts
|
|
2722
3498
|
import { BrowserWindow as BrowserWindow4 } from "electron";
|
|
2723
|
-
import
|
|
3499
|
+
import path9 from "path";
|
|
2724
3500
|
async function createSettingsWindow(parent, rendererDir2) {
|
|
2725
3501
|
const win = new BrowserWindow4({
|
|
2726
3502
|
width: 420,
|
|
@@ -2730,9 +3506,15 @@ async function createSettingsWindow(parent, rendererDir2) {
|
|
|
2730
3506
|
parent: parent ?? void 0,
|
|
2731
3507
|
title: "\u5F00\u53D1\u5DE5\u5177\u8BBE\u7F6E",
|
|
2732
3508
|
backgroundColor: themeBg(),
|
|
2733
|
-
webPreferences: {
|
|
3509
|
+
webPreferences: {
|
|
3510
|
+
nodeIntegration: false,
|
|
3511
|
+
contextIsolation: true,
|
|
3512
|
+
sandbox: false,
|
|
3513
|
+
preload: mainPreloadPath
|
|
3514
|
+
}
|
|
2734
3515
|
});
|
|
2735
|
-
|
|
3516
|
+
applyNavigationHardening(win.webContents, rendererDir2);
|
|
3517
|
+
await win.loadFile(path9.join(rendererDir2, "entries/workbench-settings/index.html"));
|
|
2736
3518
|
return win;
|
|
2737
3519
|
}
|
|
2738
3520
|
|
|
@@ -2745,13 +3527,16 @@ function wireSettingsWindowEvents(win, onClosed) {
|
|
|
2745
3527
|
|
|
2746
3528
|
// src/main/app/launch.ts
|
|
2747
3529
|
function launch(config = {}) {
|
|
2748
|
-
createWorkbenchApp(config).start();
|
|
3530
|
+
return createWorkbenchApp(config).start();
|
|
2749
3531
|
}
|
|
2750
3532
|
async function openSettingsWindow(ctx) {
|
|
2751
3533
|
if (!ctx.workbenchSettingsWindow || ctx.workbenchSettingsWindow.isDestroyed()) {
|
|
2752
|
-
|
|
2753
|
-
|
|
3534
|
+
const win = await createSettingsWindow(ctx.mainWindow, ctx.rendererDir);
|
|
3535
|
+
ctx.workbenchSettingsWindow = win;
|
|
3536
|
+
ctx.windows.setSettingsWindow(win);
|
|
3537
|
+
wireSettingsWindowEvents(win, () => {
|
|
2754
3538
|
ctx.workbenchSettingsWindow = null;
|
|
3539
|
+
ctx.windows.setSettingsWindow(null);
|
|
2755
3540
|
});
|
|
2756
3541
|
}
|
|
2757
3542
|
ctx.workbenchSettingsWindow.show();
|