@dimina-kit/devtools 0.3.2-dev.20260525152421 → 0.3.2-dev.20260601115343
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 +20 -7
- package/dist/main/api.d.ts +1 -1
- package/dist/main/app/app.js +17 -0
- package/dist/main/index.bundle.js +1006 -584
- package/dist/main/ipc/project-fs.d.ts +57 -0
- package/dist/main/ipc/project-fs.js +448 -0
- package/dist/main/ipc/simulator-module.js +2 -0
- package/dist/main/ipc/views.d.ts +19 -0
- package/dist/main/ipc/views.js +27 -0
- package/dist/main/services/layout/index.d.ts +1 -1
- package/dist/main/services/layout/index.js +3 -3
- package/dist/main/services/simulator-temp-files/fs-channels.js +1 -1
- package/dist/main/services/simulator-temp-files/request-handler.js +1 -1
- package/dist/main/services/toolbar/toolbar-store.d.ts +3 -3
- package/dist/main/services/toolbar/toolbar-store.js +18 -1
- package/dist/main/services/update/update-manager.d.ts +10 -0
- package/dist/main/services/update/update-manager.js +16 -3
- package/dist/main/services/views/view-manager.d.ts +17 -4
- package/dist/main/services/views/view-manager.js +71 -14
- package/dist/main/services/workbench-context.d.ts +6 -1
- package/dist/main/services/workspace/workspace-service.d.ts +8 -0
- package/dist/main/services/workspace/workspace-service.js +22 -4
- package/dist/main/utils/ipc-registry.d.ts +10 -1
- package/dist/main/utils/ipc-registry.js +43 -0
- package/dist/preload/shared/expose.js +2 -2
- package/dist/preload/windows/main.cjs +4 -0
- package/dist/preload/windows/main.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +2 -2
- package/dist/renderer/assets/abap-DiwvWnMr.js +2 -0
- package/dist/renderer/assets/apex-CmtZjKlf.js +2 -0
- package/dist/renderer/assets/azcli-DL2My_i-.js +2 -0
- package/dist/renderer/assets/bat-B-nC98wG.js +2 -0
- package/dist/renderer/assets/bicep-Ju5MwOgh.js +3 -0
- package/dist/renderer/assets/cameligo-8Eu1TyBr.js +2 -0
- package/dist/renderer/assets/chunk-DPI0Y_ll.js +1 -0
- package/dist/renderer/assets/clojure-u-RpMkH3.js +2 -0
- package/dist/renderer/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/dist/renderer/assets/coffee-CdA7bbTe.js +2 -0
- package/dist/renderer/assets/constants-CLAQX6VZ.js +2 -0
- package/dist/renderer/assets/cpp-CzNFP8ks.js +2 -0
- package/dist/renderer/assets/csharp-j1LThmcE.js +2 -0
- package/dist/renderer/assets/csp-CLRC61y6.js +2 -0
- package/dist/renderer/assets/css-r6rC_7P2.js +4 -0
- package/dist/renderer/assets/css.worker-DyDu5ynT.js +90 -0
- package/dist/renderer/assets/cssMode-BCBgxrnA.js +2 -0
- package/dist/renderer/assets/cypher-CW08XVUh.js +2 -0
- package/dist/renderer/assets/dart-Cs9aL5T_.js +2 -0
- package/dist/renderer/assets/dockerfile-BWM0M184.js +2 -0
- package/dist/renderer/assets/ecl-MJJuer5P.js +2 -0
- package/dist/renderer/assets/editor-CWI9TnMM.css +1 -0
- package/dist/renderer/assets/editor.api2-BTaYmRq7.js +873 -0
- package/dist/renderer/assets/editor.worker-Dpt_xPrs.js +27 -0
- package/dist/renderer/assets/elixir-D2AIuXqn.js +2 -0
- package/dist/renderer/assets/flow9-B2H24giC.js +2 -0
- package/dist/renderer/assets/freemarker2-DGDGzimW.js +4 -0
- package/dist/renderer/assets/fsharp-CMk2OIJN.js +2 -0
- package/dist/renderer/assets/go-BrMkuJg0.js +2 -0
- package/dist/renderer/assets/graphql-PSR1UKGv.js +2 -0
- package/dist/renderer/assets/handlebars-bOHhw3EG.js +2 -0
- package/dist/renderer/assets/hcl-DAQrbDOW.js +2 -0
- package/dist/renderer/assets/html-BMyuX0_c.js +2 -0
- package/dist/renderer/assets/html.worker-IWNXhnIC.js +503 -0
- package/dist/renderer/assets/htmlMode-DR4kXJ7l.js +2 -0
- package/dist/renderer/assets/index-BDMxXUfn.js +50 -0
- package/dist/renderer/assets/ini-0TG5BxW0.js +2 -0
- package/dist/renderer/assets/input-CCYS8Ave.js +2 -0
- package/dist/renderer/assets/{ipc-transport-BzPzxPwf.js → ipc-transport-Bn4KXvhu.js} +5 -5
- package/dist/renderer/assets/ipc-transport-Cu7tubyg.css +1 -0
- package/dist/renderer/assets/java-rgorz17v.js +2 -0
- package/dist/renderer/assets/javascript-Bu-tl1P7.js +2 -0
- package/dist/renderer/assets/json.worker-c4h5s80L.js +59 -0
- package/dist/renderer/assets/jsonMode-MQV_ffLR.js +8 -0
- package/dist/renderer/assets/julia-C8VMdHm8.js +2 -0
- package/dist/renderer/assets/kotlin-AhhC4Qhm.js +2 -0
- package/dist/renderer/assets/less-Cgca25AP.js +3 -0
- package/dist/renderer/assets/lexon-D0GHdBaw.js +2 -0
- package/dist/renderer/assets/liquid-ClkfOvhB.js +2 -0
- package/dist/renderer/assets/lspLanguageFeatures-Bke_4Ie9.js +5 -0
- package/dist/renderer/assets/lua-DmRsNG-P.js +2 -0
- package/dist/renderer/assets/m3-BgL5dNKT.js +2 -0
- package/dist/renderer/assets/markdown-BuJfycGS.js +2 -0
- package/dist/renderer/assets/mdx-RZdUskPu.js +2 -0
- package/dist/renderer/assets/mips-C9m_93PR.js +2 -0
- package/dist/renderer/assets/monaco.contribution-B7rUMaEv.js +3 -0
- package/dist/renderer/assets/msdax-CpFHC9OI.js +2 -0
- package/dist/renderer/assets/mysql-qFvltsqN.js +2 -0
- package/dist/renderer/assets/objective-c-Bnmr858J.js +2 -0
- package/dist/renderer/assets/pascal-WP0_D5AO.js +2 -0
- package/dist/renderer/assets/pascaligo-Blom4Rij.js +2 -0
- package/dist/renderer/assets/perl-B-vk8g64.js +2 -0
- package/dist/renderer/assets/pgsql-Cgvz6v67.js +2 -0
- package/dist/renderer/assets/php-8a3Lrw9m.js +2 -0
- package/dist/renderer/assets/pla-DuFqEZ8V.js +2 -0
- package/dist/renderer/assets/popover-CH4rwpdt.js +2 -0
- package/dist/renderer/assets/postiats-DkLtSgkp.js +2 -0
- package/dist/renderer/assets/powerquery-BJ1aNepW.js +2 -0
- package/dist/renderer/assets/powershell-rE98k687.js +2 -0
- package/dist/renderer/assets/protobuf-CUheFacr.js +3 -0
- package/dist/renderer/assets/pug-LDcAMD8w.js +2 -0
- package/dist/renderer/assets/python-Cobrm_-8.js +2 -0
- package/dist/renderer/assets/qsharp-DUKSQoR1.js +2 -0
- package/dist/renderer/assets/r-D-QApv87.js +2 -0
- package/dist/renderer/assets/razor-BEUIQFNJ.js +2 -0
- package/dist/renderer/assets/redis-SXdDyWR9.js +2 -0
- package/dist/renderer/assets/redshift-Y6lsCryn.js +2 -0
- package/dist/renderer/assets/restructuredtext-edObr9a8.js +2 -0
- package/dist/renderer/assets/ruby-CNnUfF-8.js +2 -0
- package/dist/renderer/assets/rust-IHUZWzBr.js +2 -0
- package/dist/renderer/assets/sb-DrUvY44N.js +2 -0
- package/dist/renderer/assets/scala-B4hbXGLM.js +2 -0
- package/dist/renderer/assets/scheme-BGrd12j3.js +2 -0
- package/dist/renderer/assets/scss-x5G1ES4U.js +4 -0
- package/dist/renderer/assets/select-Ct4-sijm.js +2 -0
- package/dist/renderer/assets/settings-CMTB-t0x.js +2 -0
- package/dist/renderer/assets/{settings-api-BXCw9vOZ.js → settings-api-Csdngf9Z.js} +2 -2
- package/dist/renderer/assets/shell-DOehe2Y8.js +2 -0
- package/dist/renderer/assets/solidity-BeRvcwWV.js +2 -0
- package/dist/renderer/assets/sophia-DZbkUNjy.js +2 -0
- package/dist/renderer/assets/sparql-B7_oi5-h.js +2 -0
- package/dist/renderer/assets/sql-CTlsFWVE.js +2 -0
- package/dist/renderer/assets/st-DJVEJdPE.js +2 -0
- package/dist/renderer/assets/swift-CwhT3fYa.js +2 -0
- package/dist/renderer/assets/systemverilog-BQN63pkN.js +2 -0
- package/dist/renderer/assets/tcl-DqwfpskA.js +2 -0
- package/dist/renderer/assets/ts.worker-BwM5Ha3L.js +67720 -0
- package/dist/renderer/assets/tsMode-BHa7dl9v.js +12 -0
- package/dist/renderer/assets/twig-BiyenUgc.js +2 -0
- package/dist/renderer/assets/typescript-15aT0Yj8.js +2 -0
- package/dist/renderer/assets/typespec-CWOJribt.js +2 -0
- package/dist/renderer/assets/vb-Cq5F87m3.js +2 -0
- package/dist/renderer/assets/wgsl-BAvW2lVr.js +299 -0
- package/dist/renderer/assets/workbenchSettings-CODFCzOg.js +8 -0
- package/dist/renderer/assets/workers-CRBS0W-v.js +2 -0
- package/dist/renderer/assets/xml-bR2Zh-HV.js +2 -0
- package/dist/renderer/assets/yaml-Bdk54Qrb.js +2 -0
- package/dist/renderer/entries/main/index.html +29 -6
- package/dist/renderer/entries/popover/index.html +7 -6
- package/dist/renderer/entries/settings/index.html +6 -5
- package/dist/renderer/entries/workbench-settings/index.html +6 -5
- package/dist/shared/constants.d.ts +0 -4
- package/dist/shared/constants.js +0 -4
- package/dist/shared/ipc-channels.d.ts +24 -0
- package/dist/shared/ipc-channels.js +37 -0
- package/dist/shared/ipc-schemas.d.ts +6 -0
- package/dist/shared/ipc-schemas.js +15 -0
- package/dist/shared/types.d.ts +37 -0
- package/dist/shared/vpath.d.ts +42 -0
- package/dist/shared/vpath.js +105 -0
- package/dist/simulator/assets/simulator-B4e3c6_O.js +9 -0
- package/dist/simulator/simulator.html +1 -1
- package/package.json +3 -2
- package/dist/renderer/assets/constants-DI382mTP.js +0 -2
- package/dist/renderer/assets/index-4lkyF644.js +0 -46
- package/dist/renderer/assets/input-D4byFOLS.js +0 -2
- package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +0 -1
- package/dist/renderer/assets/popover-cxKrg_os.js +0 -2
- package/dist/renderer/assets/select-BgSkkdHW.js +0 -2
- package/dist/renderer/assets/settings-oD2K1jdT.js +0 -2
- package/dist/renderer/assets/workbenchSettings-CTNBpTft.js +0 -8
- package/dist/simulator/assets/simulator-CVOFTpSj.js +0 -9
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/main/app/bootstrap.ts
|
|
9
2
|
import { app as app2, protocol } from "electron";
|
|
10
3
|
|
|
@@ -95,109 +88,683 @@ function setupCdpPort() {
|
|
|
95
88
|
app2.commandLine.appendSwitch("remote-debugging-port", envPort);
|
|
96
89
|
return;
|
|
97
90
|
}
|
|
98
|
-
const settings = loadWorkbenchSettings();
|
|
99
|
-
if (settings.cdp.enabled || settings.mcp.enabled) {
|
|
100
|
-
app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port));
|
|
101
|
-
} else if (!app2.isPackaged) {
|
|
102
|
-
app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port || DEFAULT_CDP_PORT));
|
|
91
|
+
const settings = loadWorkbenchSettings();
|
|
92
|
+
if (settings.cdp.enabled || settings.mcp.enabled) {
|
|
93
|
+
app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port));
|
|
94
|
+
} else if (!app2.isPackaged) {
|
|
95
|
+
app2.commandLine.appendSwitch("remote-debugging-port", String(settings.cdp.port || DEFAULT_CDP_PORT));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/main/ipc/project-fs.ts
|
|
100
|
+
import fs2 from "node:fs/promises";
|
|
101
|
+
import nodeFs from "node:fs";
|
|
102
|
+
import path2 from "node:path";
|
|
103
|
+
import { z } from "zod";
|
|
104
|
+
|
|
105
|
+
// src/main/utils/ipc-registry.ts
|
|
106
|
+
import { ipcMain } from "electron";
|
|
107
|
+
|
|
108
|
+
// src/main/utils/disposable.ts
|
|
109
|
+
function toDisposable(fn) {
|
|
110
|
+
let done = false;
|
|
111
|
+
return {
|
|
112
|
+
dispose() {
|
|
113
|
+
if (done) return;
|
|
114
|
+
done = true;
|
|
115
|
+
return fn();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
var DisposableRegistry = class {
|
|
120
|
+
entries = [];
|
|
121
|
+
_disposed = false;
|
|
122
|
+
/**
|
|
123
|
+
* Number of live entries. A wrapper's `dispose` splices its entry out and
|
|
124
|
+
* `disposeAll` clears the array, so `entries.length` is the live count.
|
|
125
|
+
*/
|
|
126
|
+
get size() {
|
|
127
|
+
return this.entries.length;
|
|
128
|
+
}
|
|
129
|
+
add(d) {
|
|
130
|
+
if (this._disposed) {
|
|
131
|
+
throw new Error("cannot add to disposed registry");
|
|
132
|
+
}
|
|
133
|
+
const fn = typeof d === "function" ? d : () => d.dispose();
|
|
134
|
+
const entry = { fn, released: false };
|
|
135
|
+
this.entries.push(entry);
|
|
136
|
+
return {
|
|
137
|
+
dispose: () => {
|
|
138
|
+
if (entry.released) return;
|
|
139
|
+
entry.released = true;
|
|
140
|
+
const i = this.entries.indexOf(entry);
|
|
141
|
+
if (i >= 0) this.entries.splice(i, 1);
|
|
142
|
+
return fn();
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async disposeAll() {
|
|
147
|
+
if (this._disposed) return;
|
|
148
|
+
this._disposed = true;
|
|
149
|
+
const items = this.entries.slice().reverse();
|
|
150
|
+
this.entries = [];
|
|
151
|
+
const errors = [];
|
|
152
|
+
for (const entry of items) {
|
|
153
|
+
if (entry.released) continue;
|
|
154
|
+
entry.released = true;
|
|
155
|
+
try {
|
|
156
|
+
await entry.fn();
|
|
157
|
+
} catch (e) {
|
|
158
|
+
errors.push(e);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (errors.length > 0) {
|
|
162
|
+
throw new AggregateError(errors, "DisposableRegistry encountered errors during disposeAll");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
dispose() {
|
|
166
|
+
return this.disposeAll();
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/main/utils/ipc-schema.ts
|
|
171
|
+
var IpcValidationError = class extends Error {
|
|
172
|
+
channel;
|
|
173
|
+
paths;
|
|
174
|
+
constructor(channel, issues) {
|
|
175
|
+
const paths = issues.map((i) => i.path.map((p) => String(p)).join(".") || "<root>");
|
|
176
|
+
super(`Invalid IPC arguments for ${channel}`);
|
|
177
|
+
this.name = "IpcValidationError";
|
|
178
|
+
this.channel = channel;
|
|
179
|
+
this.paths = paths;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
function validate(channel, schema, args) {
|
|
183
|
+
const r = schema.safeParse(args);
|
|
184
|
+
if (!r.success) {
|
|
185
|
+
console.warn("[ipc] schema reject", channel, r.error.issues);
|
|
186
|
+
throw new IpcValidationError(channel, r.error.issues);
|
|
187
|
+
}
|
|
188
|
+
return r.data;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/main/utils/logger.ts
|
|
192
|
+
var LEVEL_PRIORITY = {
|
|
193
|
+
debug: 0,
|
|
194
|
+
info: 1,
|
|
195
|
+
warn: 2,
|
|
196
|
+
error: 3
|
|
197
|
+
};
|
|
198
|
+
var LEVEL_PREFIX = {
|
|
199
|
+
debug: "[DEBUG]",
|
|
200
|
+
info: "[INFO]",
|
|
201
|
+
warn: "[WARN]",
|
|
202
|
+
error: "[ERROR]"
|
|
203
|
+
};
|
|
204
|
+
var currentLevel = "debug";
|
|
205
|
+
function shouldLog(level) {
|
|
206
|
+
return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[currentLevel];
|
|
207
|
+
}
|
|
208
|
+
function formatMessage(level, tag, message) {
|
|
209
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
210
|
+
return `${ts} ${LEVEL_PREFIX[level]} [${tag}] ${message}`;
|
|
211
|
+
}
|
|
212
|
+
function createLogger(tag) {
|
|
213
|
+
return {
|
|
214
|
+
debug(message, ...args) {
|
|
215
|
+
if (shouldLog("debug")) console.debug(formatMessage("debug", tag, message), ...args);
|
|
216
|
+
},
|
|
217
|
+
info(message, ...args) {
|
|
218
|
+
if (shouldLog("info")) console.info(formatMessage("info", tag, message), ...args);
|
|
219
|
+
},
|
|
220
|
+
warn(message, ...args) {
|
|
221
|
+
if (shouldLog("warn")) console.warn(formatMessage("warn", tag, message), ...args);
|
|
222
|
+
},
|
|
223
|
+
error(message, ...args) {
|
|
224
|
+
if (shouldLog("error")) console.error(formatMessage("error", tag, message), ...args);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/main/utils/ipc-registry.ts
|
|
230
|
+
var log = createLogger("ipc");
|
|
231
|
+
function reportListenerError(channel, err) {
|
|
232
|
+
if (err instanceof IpcValidationError) {
|
|
233
|
+
log.warn(`schema reject on '${channel}' at [${err.paths.join(", ")}]`);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (err instanceof Error) {
|
|
237
|
+
log.error(`listener threw on '${channel}': ${err.message}`, err.stack);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
log.error(`listener threw on '${channel}'`, err);
|
|
241
|
+
}
|
|
242
|
+
function isThenable(v) {
|
|
243
|
+
return v != null && (typeof v === "object" || typeof v === "function") && typeof v.then === "function";
|
|
244
|
+
}
|
|
245
|
+
function summarizeSender(sender) {
|
|
246
|
+
if (sender.isDestroyed()) return "<destroyed>";
|
|
247
|
+
const url = sender.getURL();
|
|
248
|
+
return `id=${sender.id} ${url.slice(0, 120)}`;
|
|
249
|
+
}
|
|
250
|
+
var IpcRegistry = class {
|
|
251
|
+
constructor(policy) {
|
|
252
|
+
this.policy = policy;
|
|
253
|
+
}
|
|
254
|
+
policy;
|
|
255
|
+
registry = new DisposableRegistry();
|
|
256
|
+
handle(channel, fn) {
|
|
257
|
+
const policy = this.policy;
|
|
258
|
+
const guarded = policy ? async (event, ...args) => {
|
|
259
|
+
const sender = event.sender;
|
|
260
|
+
if (!policy(sender)) {
|
|
261
|
+
console.warn(
|
|
262
|
+
`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`
|
|
263
|
+
);
|
|
264
|
+
throw new Error(`IPC sender rejected for channel ${channel}`);
|
|
265
|
+
}
|
|
266
|
+
return fn(event, ...args);
|
|
267
|
+
} : fn;
|
|
268
|
+
ipcMain.handle(channel, guarded);
|
|
269
|
+
this.registry.add(() => ipcMain.removeHandler(channel));
|
|
270
|
+
return this;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Register a SYNCHRONOUS `ipcRenderer.sendSync` handler. Unlike {@link on}
|
|
274
|
+
* (fire-and-forget), the renderer is blocked until we set `event.returnValue`,
|
|
275
|
+
* so `fn` MUST be synchronous and return the value to hand back. The sender
|
|
276
|
+
* policy gates it like {@link handle}; a rejected sender or a thrown error
|
|
277
|
+
* yields a structured `{ ok: false, code, message }` returnValue instead of
|
|
278
|
+
* the silent drop `on` uses, so the blocked renderer always gets an answer.
|
|
279
|
+
*/
|
|
280
|
+
handleSync(channel, fn) {
|
|
281
|
+
const policy = this.policy;
|
|
282
|
+
const listener = (event, ...args) => {
|
|
283
|
+
try {
|
|
284
|
+
if (policy && !policy(event.sender)) {
|
|
285
|
+
console.warn(
|
|
286
|
+
`[ipc] sender rejected for channel '${channel}' (${summarizeSender(event.sender)})`
|
|
287
|
+
);
|
|
288
|
+
event.returnValue = {
|
|
289
|
+
ok: false,
|
|
290
|
+
code: "EREJECTED",
|
|
291
|
+
message: `IPC sender rejected for channel ${channel}`
|
|
292
|
+
};
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
event.returnValue = fn(event, ...args);
|
|
296
|
+
} catch (err) {
|
|
297
|
+
reportListenerError(channel, err);
|
|
298
|
+
const code = err?.code;
|
|
299
|
+
event.returnValue = {
|
|
300
|
+
ok: false,
|
|
301
|
+
code: typeof code === "string" ? code : "EUNKNOWN",
|
|
302
|
+
message: err instanceof Error ? err.message : String(err)
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
ipcMain.on(channel, listener);
|
|
307
|
+
this.registry.add(() => {
|
|
308
|
+
ipcMain.removeListener(channel, listener);
|
|
309
|
+
});
|
|
310
|
+
return this;
|
|
311
|
+
}
|
|
312
|
+
on(channel, fn) {
|
|
313
|
+
const policy = this.policy;
|
|
314
|
+
const raw = fn;
|
|
315
|
+
const safeInvoke = (event, args) => {
|
|
316
|
+
try {
|
|
317
|
+
const ret = raw(event, ...args);
|
|
318
|
+
if (isThenable(ret)) {
|
|
319
|
+
Promise.resolve(ret).catch((err) => reportListenerError(channel, err));
|
|
320
|
+
}
|
|
321
|
+
} catch (err) {
|
|
322
|
+
reportListenerError(channel, err);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
const guarded = policy ? (event, ...args) => {
|
|
326
|
+
const sender = event.sender;
|
|
327
|
+
if (!policy(sender)) {
|
|
328
|
+
console.warn(
|
|
329
|
+
`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`
|
|
330
|
+
);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
safeInvoke(event, args);
|
|
334
|
+
} : (event, ...args) => safeInvoke(event, args);
|
|
335
|
+
ipcMain.on(channel, guarded);
|
|
336
|
+
this.registry.add(() => {
|
|
337
|
+
ipcMain.removeListener(channel, guarded);
|
|
338
|
+
});
|
|
339
|
+
return this;
|
|
340
|
+
}
|
|
341
|
+
dispose() {
|
|
342
|
+
return this.registry.dispose();
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// src/shared/ipc-channels.ts
|
|
347
|
+
var SimulatorChannel = {
|
|
348
|
+
Attach: "simulator:attach",
|
|
349
|
+
Detach: "simulator:detach",
|
|
350
|
+
Resize: "simulator:resize",
|
|
351
|
+
SetVisible: "simulator:setVisible",
|
|
352
|
+
Console: "simulator:console"
|
|
353
|
+
};
|
|
354
|
+
var SimulatorCustomApiChannel = {
|
|
355
|
+
List: "simulator:custom-apis:list",
|
|
356
|
+
Invoke: "simulator:custom-apis:invoke"
|
|
357
|
+
};
|
|
358
|
+
var SimulatorStorageChannel = {
|
|
359
|
+
GetSnapshot: "simulator:storage:snapshot",
|
|
360
|
+
GetActivePrefix: "simulator:storage:activePrefix",
|
|
361
|
+
Set: "simulator:storage:set",
|
|
362
|
+
Remove: "simulator:storage:remove",
|
|
363
|
+
Clear: "simulator:storage:clear",
|
|
364
|
+
ClearAll: "simulator:storage:clearAll",
|
|
365
|
+
Event: "simulator:storage:event"
|
|
366
|
+
};
|
|
367
|
+
var SimulatorElementChannel = {
|
|
368
|
+
Inspect: "simulator:element:inspect",
|
|
369
|
+
Clear: "simulator:element:clear"
|
|
370
|
+
};
|
|
371
|
+
var WorkbenchSettingsChannel = {
|
|
372
|
+
Get: "workbenchSettings:get",
|
|
373
|
+
Save: "workbenchSettings:save",
|
|
374
|
+
SetTheme: "workbenchSettings:setTheme",
|
|
375
|
+
GetCdpStatus: "workbenchSettings:getCdpStatus",
|
|
376
|
+
GetMcpStatus: "workbenchSettings:getMcpStatus",
|
|
377
|
+
SetVisible: "workbenchSettings:setVisible",
|
|
378
|
+
Init: "workbenchSettings:init"
|
|
379
|
+
};
|
|
380
|
+
var ProjectChannel = {
|
|
381
|
+
Open: "project:open",
|
|
382
|
+
Close: "project:close",
|
|
383
|
+
GetPages: "project:getPages",
|
|
384
|
+
GetCompileConfig: "project:getCompileConfig",
|
|
385
|
+
SaveCompileConfig: "project:saveCompileConfig",
|
|
386
|
+
Status: "project:status",
|
|
387
|
+
CaptureThumbnail: "project:captureThumbnail",
|
|
388
|
+
GetThumbnail: "project:getThumbnail"
|
|
389
|
+
};
|
|
390
|
+
var ProjectFsChannel = {
|
|
391
|
+
GetRoot: "project:fs:getRoot",
|
|
392
|
+
ReadFile: "project:fs:readFile",
|
|
393
|
+
WriteFile: "project:fs:writeFile",
|
|
394
|
+
/**
|
|
395
|
+
* Synchronous (blocking) write, used ONLY by the editor's `beforeunload`
|
|
396
|
+
* flush: a hard window/app close tears the renderer down before an async
|
|
397
|
+
* `WriteFile` IPC can round-trip, so the last in-debounce-window edit would
|
|
398
|
+
* be lost. `sendSync` blocks page teardown until the bytes hit disk. Runs the
|
|
399
|
+
* SAME sandbox as `WriteFile` (see project-fs `writeFileSync`).
|
|
400
|
+
*/
|
|
401
|
+
WriteFileSync: "project:fs:writeFileSync",
|
|
402
|
+
ListFiles: "project:fs:listFiles"
|
|
403
|
+
};
|
|
404
|
+
var ProjectsChannel = {
|
|
405
|
+
List: "projects:list",
|
|
406
|
+
Add: "projects:add",
|
|
407
|
+
Remove: "projects:remove",
|
|
408
|
+
/** Phase 3: merged + sanitized template catalog for the create-project dialog. */
|
|
409
|
+
ListTemplates: "projects:listTemplates",
|
|
410
|
+
/** Phase 3: host-supplied create-project dialog hook (returns input or null). */
|
|
411
|
+
OpenCreateDialog: "projects:openCreateDialog",
|
|
412
|
+
/** Phase 3: server-side scaffold + register (delegates to create-project-service). */
|
|
413
|
+
Create: "projects:create",
|
|
414
|
+
/** Default values used to pre-fill the create-project dialog (baseDir). */
|
|
415
|
+
GetCreateDefaults: "projects:getCreateDefaults"
|
|
416
|
+
};
|
|
417
|
+
var DialogChannel = {
|
|
418
|
+
OpenDirectory: "dialog:openDirectory"
|
|
419
|
+
};
|
|
420
|
+
var PanelChannel = {
|
|
421
|
+
List: "panel:list",
|
|
422
|
+
Eval: "panel:eval",
|
|
423
|
+
Select: "panel:select",
|
|
424
|
+
SelectSimulator: "panel:selectSimulator"
|
|
425
|
+
};
|
|
426
|
+
var ViewChannel = {
|
|
427
|
+
/** Update the simulator DevTools view's bounds (or hide if w/h = 0). */
|
|
428
|
+
SimulatorDevtoolsBounds: "view:simulator:devtools-bounds"
|
|
429
|
+
};
|
|
430
|
+
var PopoverChannel = {
|
|
431
|
+
Show: "popover:show",
|
|
432
|
+
Hide: "popover:hide",
|
|
433
|
+
Relaunch: "popover:relaunch",
|
|
434
|
+
Closed: "popover:closed",
|
|
435
|
+
Init: "popover:init"
|
|
436
|
+
};
|
|
437
|
+
var ToolbarChannel = {
|
|
438
|
+
GetActions: "toolbar:getActions",
|
|
439
|
+
ActionsChanged: "toolbar:actionsChanged",
|
|
440
|
+
/** Invoke a toolbar action by id: `invoke(Invoke, actionId)`. */
|
|
441
|
+
Invoke: "toolbar:invoke"
|
|
442
|
+
};
|
|
443
|
+
var WindowChannel = {
|
|
444
|
+
NavigateBack: "window:navigateBack"
|
|
445
|
+
};
|
|
446
|
+
var AppChannel = {
|
|
447
|
+
GetPreloadPath: "app:getPreloadPath",
|
|
448
|
+
GetBranding: "app:getBranding",
|
|
449
|
+
GetHeaderHeight: "app:getHeaderHeight"
|
|
450
|
+
};
|
|
451
|
+
var AutomationChannel = {
|
|
452
|
+
GetPort: "automation:port"
|
|
453
|
+
};
|
|
454
|
+
var SettingsChannel = {
|
|
455
|
+
SetVisible: "settings:setVisible",
|
|
456
|
+
ConfigChanged: "settings:configChanged",
|
|
457
|
+
ProjectSettingsChanged: "settings:projectSettingsChanged",
|
|
458
|
+
Init: "settings:init",
|
|
459
|
+
Closed: "settings:closed",
|
|
460
|
+
Changed: "settings:changed"
|
|
461
|
+
};
|
|
462
|
+
var UpdateChannel = {
|
|
463
|
+
Check: "updates:check",
|
|
464
|
+
Download: "updates:download",
|
|
465
|
+
Install: "updates:install",
|
|
466
|
+
DownloadProgress: "updates:downloadProgress",
|
|
467
|
+
Available: "updates:available"
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// src/main/ipc/project-fs.ts
|
|
471
|
+
var LIST_FILE_LIMIT = 5e3;
|
|
472
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
473
|
+
"node_modules",
|
|
474
|
+
".git",
|
|
475
|
+
".svn",
|
|
476
|
+
".hg",
|
|
477
|
+
"dist",
|
|
478
|
+
"build",
|
|
479
|
+
".next",
|
|
480
|
+
".cache",
|
|
481
|
+
".turbo",
|
|
482
|
+
".parcel-cache",
|
|
483
|
+
".nuxt",
|
|
484
|
+
".output",
|
|
485
|
+
".vite"
|
|
486
|
+
]);
|
|
487
|
+
var PathArg = z.string().min(1);
|
|
488
|
+
var ReadFileSchema = z.tuple([PathArg]);
|
|
489
|
+
var WriteFileSchema = z.tuple([PathArg, z.string()]);
|
|
490
|
+
var ListFilesSchema = z.tuple([PathArg]);
|
|
491
|
+
function makeError(code, message) {
|
|
492
|
+
const err = new Error(message);
|
|
493
|
+
err.code = code;
|
|
494
|
+
return err;
|
|
495
|
+
}
|
|
496
|
+
function isWithin(parent, child) {
|
|
497
|
+
if (child === parent) return true;
|
|
498
|
+
const rel = path2.relative(parent, child);
|
|
499
|
+
return rel.length > 0 && !rel.startsWith(".." + path2.sep) && rel !== ".." && !path2.isAbsolute(rel);
|
|
500
|
+
}
|
|
501
|
+
function enforceWithinProjectRoot(abs, root) {
|
|
502
|
+
if (root === "") {
|
|
503
|
+
throw makeError("ENOACTIVE", "No active project \u2014 open a project before using the editor file system");
|
|
504
|
+
}
|
|
505
|
+
if (typeof abs !== "string" || abs.length === 0) {
|
|
506
|
+
throw makeError("EINVAL", "path must be a non-empty string");
|
|
507
|
+
}
|
|
508
|
+
if (abs.includes("\0") || root.includes("\0")) {
|
|
509
|
+
throw makeError("EINVAL", "path must not contain NUL bytes");
|
|
510
|
+
}
|
|
511
|
+
const resolvedRoot = path2.resolve(root);
|
|
512
|
+
const resolved = path2.resolve(abs);
|
|
513
|
+
if (!isWithin(resolvedRoot, resolved)) {
|
|
514
|
+
throw makeError("EACCES", `Path escapes project root: ${resolved}`);
|
|
515
|
+
}
|
|
516
|
+
return resolved;
|
|
517
|
+
}
|
|
518
|
+
function pickWriteRoot(absPath, roots) {
|
|
519
|
+
const resolved = path2.resolve(absPath);
|
|
520
|
+
if (roots.current !== "" && isWithin(path2.resolve(roots.current), resolved)) {
|
|
521
|
+
return roots.current;
|
|
522
|
+
}
|
|
523
|
+
if (roots.lastClosed !== "" && isWithin(path2.resolve(roots.lastClosed), resolved)) {
|
|
524
|
+
return roots.lastClosed;
|
|
525
|
+
}
|
|
526
|
+
if (roots.current === "" && roots.lastClosed === "") {
|
|
527
|
+
throw makeError("ENOACTIVE", "No active project \u2014 open a project before using the editor file system");
|
|
528
|
+
}
|
|
529
|
+
throw makeError("EACCES", `Path escapes project root: ${resolved}`);
|
|
530
|
+
}
|
|
531
|
+
async function resolveWithinProjectRoot(abs, root) {
|
|
532
|
+
const lexical = enforceWithinProjectRoot(abs, root);
|
|
533
|
+
const realRoot = await fs2.realpath(path2.resolve(root));
|
|
534
|
+
let realTarget;
|
|
535
|
+
try {
|
|
536
|
+
realTarget = await fs2.realpath(lexical);
|
|
537
|
+
} catch (err) {
|
|
538
|
+
if (err.code !== "ENOENT") throw err;
|
|
539
|
+
const parent = path2.dirname(lexical);
|
|
540
|
+
const realParent = await fs2.realpath(parent);
|
|
541
|
+
if (!isWithin(realRoot, realParent)) {
|
|
542
|
+
throw makeError("EACCES", `Path escapes project root: ${lexical}`);
|
|
543
|
+
}
|
|
544
|
+
return path2.join(realParent, path2.basename(lexical));
|
|
545
|
+
}
|
|
546
|
+
if (!isWithin(realRoot, realTarget)) {
|
|
547
|
+
throw makeError("EACCES", `Path escapes project root: ${realTarget}`);
|
|
548
|
+
}
|
|
549
|
+
return realTarget;
|
|
550
|
+
}
|
|
551
|
+
async function assertOpenedWithinRoot(safe, root) {
|
|
552
|
+
const realRoot = await fs2.realpath(path2.resolve(root));
|
|
553
|
+
const real = await fs2.realpath(safe);
|
|
554
|
+
if (!isWithin(realRoot, real)) {
|
|
555
|
+
throw makeError("EACCES", `Path escapes project root (mid-path race): ${real}`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async function readFile(root, p) {
|
|
559
|
+
const safe = await resolveWithinProjectRoot(p, root);
|
|
560
|
+
const fh = await fs2.open(safe, fs2.constants.O_RDONLY | fs2.constants.O_NOFOLLOW);
|
|
561
|
+
try {
|
|
562
|
+
await assertOpenedWithinRoot(safe, root);
|
|
563
|
+
return await fh.readFile("utf8");
|
|
564
|
+
} finally {
|
|
565
|
+
await fh.close();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async function assertWritableAncestor(lexical, root) {
|
|
569
|
+
const realRoot = await fs2.realpath(path2.resolve(root));
|
|
570
|
+
let ancestor = path2.dirname(lexical);
|
|
571
|
+
for (; ; ) {
|
|
572
|
+
let realAncestor;
|
|
573
|
+
try {
|
|
574
|
+
realAncestor = await fs2.realpath(ancestor);
|
|
575
|
+
} catch (err) {
|
|
576
|
+
if (err.code !== "ENOENT") throw err;
|
|
577
|
+
const next = path2.dirname(ancestor);
|
|
578
|
+
if (next === ancestor) {
|
|
579
|
+
throw makeError("EACCES", `Path escapes project root: ${lexical}`);
|
|
580
|
+
}
|
|
581
|
+
ancestor = next;
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
if (!isWithin(realRoot, realAncestor)) {
|
|
585
|
+
throw makeError("EACCES", `Path escapes project root: ${lexical}`);
|
|
586
|
+
}
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
async function writeFile(root, p, content) {
|
|
591
|
+
const lexical = enforceWithinProjectRoot(p, root);
|
|
592
|
+
await assertWritableAncestor(lexical, root);
|
|
593
|
+
await fs2.mkdir(path2.dirname(lexical), { recursive: true });
|
|
594
|
+
const safe = await resolveWithinProjectRoot(p, root);
|
|
595
|
+
const fh = await fs2.open(
|
|
596
|
+
safe,
|
|
597
|
+
fs2.constants.O_WRONLY | fs2.constants.O_CREAT | fs2.constants.O_NOFOLLOW,
|
|
598
|
+
438
|
|
599
|
+
);
|
|
600
|
+
try {
|
|
601
|
+
await assertOpenedWithinRoot(safe, root);
|
|
602
|
+
const buf = Buffer.from(content, "utf8");
|
|
603
|
+
let off = 0;
|
|
604
|
+
while (off < buf.length) {
|
|
605
|
+
const { bytesWritten } = await fh.write(buf, off, buf.length - off, off);
|
|
606
|
+
if (bytesWritten <= 0) break;
|
|
607
|
+
off += bytesWritten;
|
|
608
|
+
}
|
|
609
|
+
await fh.truncate(buf.length);
|
|
610
|
+
} finally {
|
|
611
|
+
await fh.close();
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function resolveWithinProjectRootSync(abs, root) {
|
|
615
|
+
const lexical = enforceWithinProjectRoot(abs, root);
|
|
616
|
+
const realRoot = nodeFs.realpathSync(path2.resolve(root));
|
|
617
|
+
let realTarget;
|
|
618
|
+
try {
|
|
619
|
+
realTarget = nodeFs.realpathSync(lexical);
|
|
620
|
+
} catch (err) {
|
|
621
|
+
if (err.code !== "ENOENT") throw err;
|
|
622
|
+
const parent = path2.dirname(lexical);
|
|
623
|
+
const realParent = nodeFs.realpathSync(parent);
|
|
624
|
+
if (!isWithin(realRoot, realParent)) {
|
|
625
|
+
throw makeError("EACCES", `Path escapes project root: ${lexical}`);
|
|
626
|
+
}
|
|
627
|
+
return path2.join(realParent, path2.basename(lexical));
|
|
628
|
+
}
|
|
629
|
+
if (!isWithin(realRoot, realTarget)) {
|
|
630
|
+
throw makeError("EACCES", `Path escapes project root: ${realTarget}`);
|
|
631
|
+
}
|
|
632
|
+
return realTarget;
|
|
633
|
+
}
|
|
634
|
+
function assertWritableAncestorSync(lexical, root) {
|
|
635
|
+
const realRoot = nodeFs.realpathSync(path2.resolve(root));
|
|
636
|
+
let ancestor = path2.dirname(lexical);
|
|
637
|
+
for (; ; ) {
|
|
638
|
+
let realAncestor;
|
|
639
|
+
try {
|
|
640
|
+
realAncestor = nodeFs.realpathSync(ancestor);
|
|
641
|
+
} catch (err) {
|
|
642
|
+
if (err.code !== "ENOENT") throw err;
|
|
643
|
+
const next = path2.dirname(ancestor);
|
|
644
|
+
if (next === ancestor) {
|
|
645
|
+
throw makeError("EACCES", `Path escapes project root: ${lexical}`);
|
|
646
|
+
}
|
|
647
|
+
ancestor = next;
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (!isWithin(realRoot, realAncestor)) {
|
|
651
|
+
throw makeError("EACCES", `Path escapes project root: ${lexical}`);
|
|
652
|
+
}
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
function assertOpenedWithinRootSync(safe, root) {
|
|
657
|
+
const realRoot = nodeFs.realpathSync(path2.resolve(root));
|
|
658
|
+
const real = nodeFs.realpathSync(safe);
|
|
659
|
+
if (!isWithin(realRoot, real)) {
|
|
660
|
+
throw makeError("EACCES", `Path escapes project root (mid-path race): ${real}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function writeFileSync(root, p, content) {
|
|
664
|
+
const lexical = enforceWithinProjectRoot(p, root);
|
|
665
|
+
assertWritableAncestorSync(lexical, root);
|
|
666
|
+
nodeFs.mkdirSync(path2.dirname(lexical), { recursive: true });
|
|
667
|
+
const safe = resolveWithinProjectRootSync(p, root);
|
|
668
|
+
const fd = nodeFs.openSync(
|
|
669
|
+
safe,
|
|
670
|
+
nodeFs.constants.O_WRONLY | nodeFs.constants.O_CREAT | nodeFs.constants.O_NOFOLLOW,
|
|
671
|
+
438
|
|
672
|
+
);
|
|
673
|
+
try {
|
|
674
|
+
assertOpenedWithinRootSync(safe, root);
|
|
675
|
+
const buf = Buffer.from(content, "utf8");
|
|
676
|
+
let off = 0;
|
|
677
|
+
while (off < buf.length) {
|
|
678
|
+
const bytesWritten = nodeFs.writeSync(fd, buf, off, buf.length - off, off);
|
|
679
|
+
if (bytesWritten <= 0) break;
|
|
680
|
+
off += bytesWritten;
|
|
681
|
+
}
|
|
682
|
+
nodeFs.ftruncateSync(fd, buf.length);
|
|
683
|
+
} finally {
|
|
684
|
+
nodeFs.closeSync(fd);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
async function listFiles(root, rootArg) {
|
|
688
|
+
const safeRoot = await resolveWithinProjectRoot(rootArg, root);
|
|
689
|
+
const out = [];
|
|
690
|
+
const queue = [safeRoot];
|
|
691
|
+
while (queue.length > 0) {
|
|
692
|
+
if (out.length >= LIST_FILE_LIMIT) break;
|
|
693
|
+
const dir = queue.shift();
|
|
694
|
+
let entries;
|
|
695
|
+
try {
|
|
696
|
+
entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
697
|
+
} catch (err) {
|
|
698
|
+
const code = err.code;
|
|
699
|
+
if (code === "EACCES" || code === "ENOENT" || code === "ENOTDIR") continue;
|
|
700
|
+
throw err;
|
|
701
|
+
}
|
|
702
|
+
for (const e of entries) {
|
|
703
|
+
if (out.length >= LIST_FILE_LIMIT) break;
|
|
704
|
+
const full = path2.join(dir, e.name);
|
|
705
|
+
if (e.isDirectory()) {
|
|
706
|
+
if (SKIP_DIRS.has(e.name)) continue;
|
|
707
|
+
queue.push(full);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
if (!e.isFile()) continue;
|
|
711
|
+
out.push(path2.relative(safeRoot, full).split(path2.sep).join("/"));
|
|
712
|
+
}
|
|
103
713
|
}
|
|
714
|
+
return out;
|
|
715
|
+
}
|
|
716
|
+
function registerProjectFsIpc(ctx) {
|
|
717
|
+
const getRoot = () => ctx.workspace.getProjectPath();
|
|
718
|
+
const getLastClosedRoot = () => ctx.workspace.getLastClosedProjectPath();
|
|
719
|
+
return new IpcRegistry(ctx.senderPolicy).handle(ProjectFsChannel.GetRoot, () => getRoot()).handle(ProjectFsChannel.ReadFile, (_event, ...args) => {
|
|
720
|
+
const [p] = validate(ProjectFsChannel.ReadFile, ReadFileSchema, args);
|
|
721
|
+
return readFile(getRoot(), p);
|
|
722
|
+
}).handle(ProjectFsChannel.WriteFile, (_event, ...args) => {
|
|
723
|
+
const [p, content] = validate(ProjectFsChannel.WriteFile, WriteFileSchema, args);
|
|
724
|
+
const root = pickWriteRoot(p, { current: getRoot(), lastClosed: getLastClosedRoot() });
|
|
725
|
+
return writeFile(root, p, content);
|
|
726
|
+
}).handleSync(ProjectFsChannel.WriteFileSync, (_event, ...args) => {
|
|
727
|
+
const [p, content] = validate(ProjectFsChannel.WriteFileSync, WriteFileSchema, args);
|
|
728
|
+
const root = pickWriteRoot(p, { current: getRoot(), lastClosed: getLastClosedRoot() });
|
|
729
|
+
writeFileSync(root, p, content);
|
|
730
|
+
return { ok: true };
|
|
731
|
+
}).handle(ProjectFsChannel.ListFiles, (_event, ...args) => {
|
|
732
|
+
const [p] = validate(ProjectFsChannel.ListFiles, ListFilesSchema, args);
|
|
733
|
+
return listFiles(getRoot(), p);
|
|
734
|
+
});
|
|
104
735
|
}
|
|
105
736
|
|
|
106
737
|
// src/main/app/app.ts
|
|
107
738
|
import { app as app13, BrowserWindow as BrowserWindow4, nativeImage, session as session2 } from "electron";
|
|
108
|
-
import
|
|
109
|
-
import
|
|
739
|
+
import fs10 from "fs";
|
|
740
|
+
import path16 from "path";
|
|
110
741
|
|
|
111
742
|
// src/main/utils/paths.ts
|
|
112
|
-
import
|
|
113
|
-
import
|
|
743
|
+
import fs3 from "fs";
|
|
744
|
+
import path3 from "path";
|
|
114
745
|
import { fileURLToPath } from "url";
|
|
115
746
|
function resolveDevtoolsPackageRoot() {
|
|
116
|
-
let dir =
|
|
117
|
-
const root =
|
|
747
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
748
|
+
const root = path3.parse(dir).root;
|
|
118
749
|
while (dir !== root) {
|
|
119
|
-
const pkgPath =
|
|
750
|
+
const pkgPath = path3.join(dir, "package.json");
|
|
120
751
|
try {
|
|
121
|
-
const pkg2 = JSON.parse(
|
|
752
|
+
const pkg2 = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
|
|
122
753
|
if (pkg2.name === "@dimina-kit/devtools") return dir;
|
|
123
754
|
} catch {
|
|
124
755
|
}
|
|
125
|
-
dir =
|
|
756
|
+
dir = path3.dirname(dir);
|
|
126
757
|
}
|
|
127
758
|
throw new Error("@dimina-kit/devtools package root not found from " + fileURLToPath(import.meta.url));
|
|
128
759
|
}
|
|
129
760
|
var devtoolsPackageRoot = resolveDevtoolsPackageRoot();
|
|
130
|
-
var rendererDir =
|
|
131
|
-
var defaultPreloadPath =
|
|
132
|
-
var mainPreloadPath =
|
|
133
|
-
var simulatorDir =
|
|
761
|
+
var rendererDir = path3.join(devtoolsPackageRoot, "dist/renderer");
|
|
762
|
+
var defaultPreloadPath = path3.join(devtoolsPackageRoot, "dist/preload/windows/simulator.js");
|
|
763
|
+
var mainPreloadPath = path3.join(devtoolsPackageRoot, "dist/preload/windows/main.cjs");
|
|
764
|
+
var simulatorDir = path3.join(devtoolsPackageRoot, "dist/simulator");
|
|
134
765
|
|
|
135
766
|
// src/main/utils/theme.ts
|
|
136
767
|
import { BrowserWindow, nativeTheme as nativeTheme2 } from "electron";
|
|
137
|
-
|
|
138
|
-
// src/main/utils/disposable.ts
|
|
139
|
-
function toDisposable(fn) {
|
|
140
|
-
let done = false;
|
|
141
|
-
return {
|
|
142
|
-
dispose() {
|
|
143
|
-
if (done) return;
|
|
144
|
-
done = true;
|
|
145
|
-
return fn();
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
var DisposableRegistry = class {
|
|
150
|
-
entries = [];
|
|
151
|
-
_disposed = false;
|
|
152
|
-
/**
|
|
153
|
-
* Number of live entries. A wrapper's `dispose` splices its entry out and
|
|
154
|
-
* `disposeAll` clears the array, so `entries.length` is the live count.
|
|
155
|
-
*/
|
|
156
|
-
get size() {
|
|
157
|
-
return this.entries.length;
|
|
158
|
-
}
|
|
159
|
-
add(d) {
|
|
160
|
-
if (this._disposed) {
|
|
161
|
-
throw new Error("cannot add to disposed registry");
|
|
162
|
-
}
|
|
163
|
-
const fn = typeof d === "function" ? d : () => d.dispose();
|
|
164
|
-
const entry = { fn, released: false };
|
|
165
|
-
this.entries.push(entry);
|
|
166
|
-
return {
|
|
167
|
-
dispose: () => {
|
|
168
|
-
if (entry.released) return;
|
|
169
|
-
entry.released = true;
|
|
170
|
-
const i = this.entries.indexOf(entry);
|
|
171
|
-
if (i >= 0) this.entries.splice(i, 1);
|
|
172
|
-
return fn();
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
async disposeAll() {
|
|
177
|
-
if (this._disposed) return;
|
|
178
|
-
this._disposed = true;
|
|
179
|
-
const items = this.entries.slice().reverse();
|
|
180
|
-
this.entries = [];
|
|
181
|
-
const errors = [];
|
|
182
|
-
for (const entry of items) {
|
|
183
|
-
if (entry.released) continue;
|
|
184
|
-
entry.released = true;
|
|
185
|
-
try {
|
|
186
|
-
await entry.fn();
|
|
187
|
-
} catch (e) {
|
|
188
|
-
errors.push(e);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (errors.length > 0) {
|
|
192
|
-
throw new AggregateError(errors, "DisposableRegistry encountered errors during disposeAll");
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
dispose() {
|
|
196
|
-
return this.disposeAll();
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
// src/main/utils/theme.ts
|
|
201
768
|
function themeBg() {
|
|
202
769
|
return nativeTheme2.shouldUseDarkColors ? "#1a1a1a" : "#fafafa";
|
|
203
770
|
}
|
|
@@ -230,7 +797,7 @@ function registerAppLifecycle() {
|
|
|
230
797
|
|
|
231
798
|
// src/main/windows/main-window/create.ts
|
|
232
799
|
import { app as app4, BrowserWindow as BrowserWindow2, View, session, shell as shell2 } from "electron";
|
|
233
|
-
import
|
|
800
|
+
import path5 from "path";
|
|
234
801
|
|
|
235
802
|
// src/main/services/simulator/referer.ts
|
|
236
803
|
var DEFAULT_VERSION = "develop";
|
|
@@ -253,7 +820,7 @@ function getSimulatorServicewechatReferer() {
|
|
|
253
820
|
|
|
254
821
|
// src/main/windows/navigation-hardening.ts
|
|
255
822
|
import { shell } from "electron";
|
|
256
|
-
import
|
|
823
|
+
import path4 from "path";
|
|
257
824
|
import { pathToFileURL } from "url";
|
|
258
825
|
function handleWindowOpenExternal(url) {
|
|
259
826
|
try {
|
|
@@ -266,7 +833,7 @@ function handleWindowOpenExternal(url) {
|
|
|
266
833
|
return { action: "deny" };
|
|
267
834
|
}
|
|
268
835
|
function applyNavigationHardening(wc, rendererDir2) {
|
|
269
|
-
const rendererPrefix = pathToFileURL(rendererDir2 +
|
|
836
|
+
const rendererPrefix = pathToFileURL(rendererDir2 + path4.sep).href;
|
|
270
837
|
wc.setWindowOpenHandler(({ url }) => handleWindowOpenExternal(url));
|
|
271
838
|
wc.on("will-navigate", (event, url) => {
|
|
272
839
|
let parsed;
|
|
@@ -344,7 +911,7 @@ function createMainWindow(opts) {
|
|
|
344
911
|
}
|
|
345
912
|
}
|
|
346
913
|
});
|
|
347
|
-
const rendererDir2 =
|
|
914
|
+
const rendererDir2 = path5.dirname(opts.indexHtml);
|
|
348
915
|
applyNavigationHardening(mainWindow.webContents, rendererDir2);
|
|
349
916
|
mainWindow.webContents.on("will-attach-webview", (event, webPreferences, params) => {
|
|
350
917
|
;
|
|
@@ -393,164 +960,58 @@ function wireMainWindowEvents(win, state = {}) {
|
|
|
393
960
|
};
|
|
394
961
|
resizeMainWebView();
|
|
395
962
|
win.on("resize", resizeMainWebView);
|
|
396
|
-
registry.add(toDisposable(() => {
|
|
397
|
-
win.removeListener("resize", resizeMainWebView);
|
|
398
|
-
}));
|
|
399
|
-
if (state.onClose) {
|
|
400
|
-
const onCloseHandler = (event) => {
|
|
401
|
-
void state.onClose?.(event);
|
|
402
|
-
};
|
|
403
|
-
win.on("close", onCloseHandler);
|
|
404
|
-
registry.add(toDisposable(() => {
|
|
405
|
-
win.removeListener("close", onCloseHandler);
|
|
406
|
-
}));
|
|
407
|
-
}
|
|
408
|
-
const devToolsAccelerator = "CommandOrControl+Shift+I";
|
|
409
|
-
const registered = globalShortcut2.register(devToolsAccelerator, () => {
|
|
410
|
-
win.webContents.openDevTools({ mode: "detach" });
|
|
411
|
-
});
|
|
412
|
-
if (registered) {
|
|
413
|
-
registry.add(toDisposable(() => {
|
|
414
|
-
globalShortcut2.unregister(devToolsAccelerator);
|
|
415
|
-
}));
|
|
416
|
-
}
|
|
417
|
-
return registry;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// src/main/utils/sender-policy.ts
|
|
421
|
-
function createWorkbenchSenderPolicy(ctx) {
|
|
422
|
-
return (sender) => {
|
|
423
|
-
if (sender.isDestroyed()) return false;
|
|
424
|
-
if (ctx.windows.isMainSender(sender.id)) return true;
|
|
425
|
-
if (ctx.windows.isSettingsWindowSender(sender.id)) return true;
|
|
426
|
-
if (ctx.trustedWindowSenderIds.has(sender.id)) return true;
|
|
427
|
-
const settingsViewId = ctx.views.getSettingsWebContentsId();
|
|
428
|
-
if (settingsViewId != null && sender.id === settingsViewId) return true;
|
|
429
|
-
const popoverViewId = ctx.views.getPopoverWebContentsId();
|
|
430
|
-
if (popoverViewId != null && sender.id === popoverViewId) return true;
|
|
431
|
-
return false;
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// src/main/services/default-adapter.ts
|
|
436
|
-
import
|
|
437
|
-
import { app as app5 } from "electron";
|
|
438
|
-
var defaultAdapter = {
|
|
439
|
-
async openProject(opts) {
|
|
440
|
-
const diminaKit = await import("@dimina-kit/devkit");
|
|
441
|
-
const openProjectOpts = {
|
|
442
|
-
outputDir:
|
|
443
|
-
...opts,
|
|
444
|
-
simulatorDir
|
|
445
|
-
};
|
|
446
|
-
return diminaKit.openProject(openProjectOpts);
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
// src/shared/ipc-channels.ts
|
|
451
|
-
var SimulatorChannel = {
|
|
452
|
-
Attach: "simulator:attach",
|
|
453
|
-
Detach: "simulator:detach",
|
|
454
|
-
Resize: "simulator:resize",
|
|
455
|
-
SetVisible: "simulator:setVisible",
|
|
456
|
-
Console: "simulator:console"
|
|
457
|
-
};
|
|
458
|
-
var SimulatorCustomApiChannel = {
|
|
459
|
-
List: "simulator:custom-apis:list",
|
|
460
|
-
Invoke: "simulator:custom-apis:invoke"
|
|
461
|
-
};
|
|
462
|
-
var SimulatorStorageChannel = {
|
|
463
|
-
GetSnapshot: "simulator:storage:snapshot",
|
|
464
|
-
GetActivePrefix: "simulator:storage:activePrefix",
|
|
465
|
-
Set: "simulator:storage:set",
|
|
466
|
-
Remove: "simulator:storage:remove",
|
|
467
|
-
Clear: "simulator:storage:clear",
|
|
468
|
-
ClearAll: "simulator:storage:clearAll",
|
|
469
|
-
Event: "simulator:storage:event"
|
|
470
|
-
};
|
|
471
|
-
var SimulatorElementChannel = {
|
|
472
|
-
Inspect: "simulator:element:inspect",
|
|
473
|
-
Clear: "simulator:element:clear"
|
|
474
|
-
};
|
|
475
|
-
var WorkbenchSettingsChannel = {
|
|
476
|
-
Get: "workbenchSettings:get",
|
|
477
|
-
Save: "workbenchSettings:save",
|
|
478
|
-
SetTheme: "workbenchSettings:setTheme",
|
|
479
|
-
GetCdpStatus: "workbenchSettings:getCdpStatus",
|
|
480
|
-
GetMcpStatus: "workbenchSettings:getMcpStatus",
|
|
481
|
-
SetVisible: "workbenchSettings:setVisible",
|
|
482
|
-
Init: "workbenchSettings:init"
|
|
483
|
-
};
|
|
484
|
-
var ProjectChannel = {
|
|
485
|
-
Open: "project:open",
|
|
486
|
-
Close: "project:close",
|
|
487
|
-
GetPages: "project:getPages",
|
|
488
|
-
GetCompileConfig: "project:getCompileConfig",
|
|
489
|
-
SaveCompileConfig: "project:saveCompileConfig",
|
|
490
|
-
Status: "project:status",
|
|
491
|
-
CaptureThumbnail: "project:captureThumbnail",
|
|
492
|
-
GetThumbnail: "project:getThumbnail"
|
|
493
|
-
};
|
|
494
|
-
var ProjectsChannel = {
|
|
495
|
-
List: "projects:list",
|
|
496
|
-
Add: "projects:add",
|
|
497
|
-
Remove: "projects:remove",
|
|
498
|
-
/** Phase 3: merged + sanitized template catalog for the create-project dialog. */
|
|
499
|
-
ListTemplates: "projects:listTemplates",
|
|
500
|
-
/** Phase 3: host-supplied create-project dialog hook (returns input or null). */
|
|
501
|
-
OpenCreateDialog: "projects:openCreateDialog",
|
|
502
|
-
/** Phase 3: server-side scaffold + register (delegates to create-project-service). */
|
|
503
|
-
Create: "projects:create",
|
|
504
|
-
/** Default values used to pre-fill the create-project dialog (baseDir). */
|
|
505
|
-
GetCreateDefaults: "projects:getCreateDefaults"
|
|
506
|
-
};
|
|
507
|
-
var DialogChannel = {
|
|
508
|
-
OpenDirectory: "dialog:openDirectory"
|
|
509
|
-
};
|
|
510
|
-
var PanelChannel = {
|
|
511
|
-
List: "panel:list",
|
|
512
|
-
Eval: "panel:eval",
|
|
513
|
-
Select: "panel:select",
|
|
514
|
-
SelectSimulator: "panel:selectSimulator"
|
|
515
|
-
};
|
|
516
|
-
var PopoverChannel = {
|
|
517
|
-
Show: "popover:show",
|
|
518
|
-
Hide: "popover:hide",
|
|
519
|
-
Relaunch: "popover:relaunch",
|
|
520
|
-
Closed: "popover:closed",
|
|
521
|
-
Init: "popover:init"
|
|
522
|
-
};
|
|
523
|
-
var ToolbarChannel = {
|
|
524
|
-
GetActions: "toolbar:getActions",
|
|
525
|
-
ActionsChanged: "toolbar:actionsChanged",
|
|
526
|
-
/** Invoke a toolbar action by id: `invoke(Invoke, actionId)`. */
|
|
527
|
-
Invoke: "toolbar:invoke"
|
|
528
|
-
};
|
|
529
|
-
var WindowChannel = {
|
|
530
|
-
NavigateBack: "window:navigateBack"
|
|
531
|
-
};
|
|
532
|
-
var AppChannel = {
|
|
533
|
-
GetPreloadPath: "app:getPreloadPath",
|
|
534
|
-
GetBranding: "app:getBranding",
|
|
535
|
-
GetHeaderHeight: "app:getHeaderHeight"
|
|
536
|
-
};
|
|
537
|
-
var AutomationChannel = {
|
|
538
|
-
GetPort: "automation:port"
|
|
539
|
-
};
|
|
540
|
-
var SettingsChannel = {
|
|
541
|
-
SetVisible: "settings:setVisible",
|
|
542
|
-
ConfigChanged: "settings:configChanged",
|
|
543
|
-
ProjectSettingsChanged: "settings:projectSettingsChanged",
|
|
544
|
-
Init: "settings:init",
|
|
545
|
-
Closed: "settings:closed",
|
|
546
|
-
Changed: "settings:changed"
|
|
547
|
-
};
|
|
548
|
-
var UpdateChannel = {
|
|
549
|
-
Check: "updates:check",
|
|
550
|
-
Download: "updates:download",
|
|
551
|
-
Install: "updates:install",
|
|
552
|
-
DownloadProgress: "updates:downloadProgress",
|
|
553
|
-
Available: "updates:available"
|
|
963
|
+
registry.add(toDisposable(() => {
|
|
964
|
+
win.removeListener("resize", resizeMainWebView);
|
|
965
|
+
}));
|
|
966
|
+
if (state.onClose) {
|
|
967
|
+
const onCloseHandler = (event) => {
|
|
968
|
+
void state.onClose?.(event);
|
|
969
|
+
};
|
|
970
|
+
win.on("close", onCloseHandler);
|
|
971
|
+
registry.add(toDisposable(() => {
|
|
972
|
+
win.removeListener("close", onCloseHandler);
|
|
973
|
+
}));
|
|
974
|
+
}
|
|
975
|
+
const devToolsAccelerator = "CommandOrControl+Shift+I";
|
|
976
|
+
const registered = globalShortcut2.register(devToolsAccelerator, () => {
|
|
977
|
+
win.webContents.openDevTools({ mode: "detach" });
|
|
978
|
+
});
|
|
979
|
+
if (registered) {
|
|
980
|
+
registry.add(toDisposable(() => {
|
|
981
|
+
globalShortcut2.unregister(devToolsAccelerator);
|
|
982
|
+
}));
|
|
983
|
+
}
|
|
984
|
+
return registry;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// src/main/utils/sender-policy.ts
|
|
988
|
+
function createWorkbenchSenderPolicy(ctx) {
|
|
989
|
+
return (sender) => {
|
|
990
|
+
if (sender.isDestroyed()) return false;
|
|
991
|
+
if (ctx.windows.isMainSender(sender.id)) return true;
|
|
992
|
+
if (ctx.windows.isSettingsWindowSender(sender.id)) return true;
|
|
993
|
+
if (ctx.trustedWindowSenderIds.has(sender.id)) return true;
|
|
994
|
+
const settingsViewId = ctx.views.getSettingsWebContentsId();
|
|
995
|
+
if (settingsViewId != null && sender.id === settingsViewId) return true;
|
|
996
|
+
const popoverViewId = ctx.views.getPopoverWebContentsId();
|
|
997
|
+
if (popoverViewId != null && sender.id === popoverViewId) return true;
|
|
998
|
+
return false;
|
|
999
|
+
};
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/main/services/default-adapter.ts
|
|
1003
|
+
import path6 from "path";
|
|
1004
|
+
import { app as app5 } from "electron";
|
|
1005
|
+
var defaultAdapter = {
|
|
1006
|
+
async openProject(opts) {
|
|
1007
|
+
const diminaKit = await import("@dimina-kit/devkit");
|
|
1008
|
+
const openProjectOpts = {
|
|
1009
|
+
outputDir: path6.join(app5.getPath("userData"), "dimina-fe-output"),
|
|
1010
|
+
...opts,
|
|
1011
|
+
simulatorDir
|
|
1012
|
+
};
|
|
1013
|
+
return diminaKit.openProject(openProjectOpts);
|
|
1014
|
+
}
|
|
554
1015
|
};
|
|
555
1016
|
|
|
556
1017
|
// src/main/services/notifications/renderer-notifier.ts
|
|
@@ -609,7 +1070,7 @@ function createRendererNotifier(ctx) {
|
|
|
609
1070
|
|
|
610
1071
|
// src/main/services/views/view-manager.ts
|
|
611
1072
|
import { WebContentsView as WebContentsView2, webContents } from "electron";
|
|
612
|
-
import
|
|
1073
|
+
import path7 from "path";
|
|
613
1074
|
|
|
614
1075
|
// src/main/services/layout/index.ts
|
|
615
1076
|
var SPLITTER_W = 4;
|
|
@@ -633,7 +1094,7 @@ function computeSettingsBounds(contentWidth, contentHeight, headerHeight) {
|
|
|
633
1094
|
return {
|
|
634
1095
|
x: Math.max(0, contentWidth - SETTINGS_W),
|
|
635
1096
|
y: headerHeight,
|
|
636
|
-
width: SETTINGS_W,
|
|
1097
|
+
width: Math.min(SETTINGS_W, Math.max(1, contentWidth)),
|
|
637
1098
|
height: Math.max(1, contentHeight - headerHeight)
|
|
638
1099
|
};
|
|
639
1100
|
}
|
|
@@ -641,7 +1102,7 @@ function computePopoverBounds(contentWidth, contentHeight, headerHeight) {
|
|
|
641
1102
|
return {
|
|
642
1103
|
x: 0,
|
|
643
1104
|
y: headerHeight,
|
|
644
|
-
width: contentWidth,
|
|
1105
|
+
width: Math.max(1, contentWidth),
|
|
645
1106
|
height: contentHeight - headerHeight
|
|
646
1107
|
};
|
|
647
1108
|
}
|
|
@@ -656,6 +1117,7 @@ function createViewManager(ctx) {
|
|
|
656
1117
|
let popoverView = null;
|
|
657
1118
|
let lastSimWidth = 375;
|
|
658
1119
|
let simulatorWebContentsId = null;
|
|
1120
|
+
let simulatorBoundsOverride = null;
|
|
659
1121
|
function destroyViewInternal(view) {
|
|
660
1122
|
if (!view) return;
|
|
661
1123
|
if (!ctx.windows.mainWindow.isDestroyed()) {
|
|
@@ -673,8 +1135,37 @@ function createViewManager(ctx) {
|
|
|
673
1135
|
}
|
|
674
1136
|
function applySimulatorBounds(simWidth) {
|
|
675
1137
|
if (!simulatorView || ctx.windows.mainWindow.isDestroyed()) return;
|
|
1138
|
+
if (simulatorBoundsOverride) {
|
|
1139
|
+
simulatorView.setBounds(simulatorBoundsOverride);
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
676
1142
|
const [w = 0, h = 0] = ctx.windows.mainWindow.getContentSize();
|
|
677
|
-
simulatorView.setBounds(
|
|
1143
|
+
simulatorView.setBounds(
|
|
1144
|
+
computeSimulatorBounds(w, h, simWidth, headerHeight)
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
function isHidden(b) {
|
|
1148
|
+
return b.width <= 0 || b.height <= 0;
|
|
1149
|
+
}
|
|
1150
|
+
function setSimulatorDevtoolsBounds(bounds) {
|
|
1151
|
+
simulatorBoundsOverride = bounds;
|
|
1152
|
+
if (!simulatorView || simulatorView.webContents.isDestroyed()) return;
|
|
1153
|
+
if (ctx.windows.mainWindow.isDestroyed()) return;
|
|
1154
|
+
if (isHidden(bounds)) {
|
|
1155
|
+
if (simulatorViewAdded) {
|
|
1156
|
+
try {
|
|
1157
|
+
ctx.windows.mainWindow.contentView.removeChildView(simulatorView);
|
|
1158
|
+
} catch {
|
|
1159
|
+
}
|
|
1160
|
+
simulatorViewAdded = false;
|
|
1161
|
+
}
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
if (!simulatorViewAdded) {
|
|
1165
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
1166
|
+
simulatorViewAdded = true;
|
|
1167
|
+
}
|
|
1168
|
+
simulatorView.setBounds(bounds);
|
|
678
1169
|
}
|
|
679
1170
|
function applySettingsBounds() {
|
|
680
1171
|
if (!settingsView || ctx.windows.mainWindow.isDestroyed()) return;
|
|
@@ -715,25 +1206,35 @@ function createViewManager(ctx) {
|
|
|
715
1206
|
let tries = 0
|
|
716
1207
|
const timer = setInterval(() => {
|
|
717
1208
|
tries++
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
1209
|
+
try {
|
|
1210
|
+
const UI = globalThis.UI
|
|
1211
|
+
const vm = UI && UI.ViewManager && typeof UI.ViewManager.instance === 'function'
|
|
1212
|
+
? UI.ViewManager.instance()
|
|
1213
|
+
: null
|
|
1214
|
+
if (vm && typeof vm.showView === 'function') {
|
|
1215
|
+
vm.showView('console')
|
|
723
1216
|
clearInterval(timer)
|
|
724
1217
|
return
|
|
725
1218
|
}
|
|
726
|
-
}
|
|
727
|
-
if (tries >
|
|
728
|
-
},
|
|
1219
|
+
} catch {}
|
|
1220
|
+
if (tries > 80) clearInterval(timer)
|
|
1221
|
+
}, 50)
|
|
729
1222
|
})()
|
|
730
1223
|
`).catch(() => {
|
|
731
1224
|
});
|
|
732
1225
|
});
|
|
733
1226
|
if (getDefaultTab(ctx) === "simulator") {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
1227
|
+
if (simulatorBoundsOverride) {
|
|
1228
|
+
if (!isHidden(simulatorBoundsOverride)) {
|
|
1229
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
1230
|
+
simulatorViewAdded = true;
|
|
1231
|
+
simulatorView.setBounds(simulatorBoundsOverride);
|
|
1232
|
+
}
|
|
1233
|
+
} else {
|
|
1234
|
+
ctx.windows.mainWindow.contentView.addChildView(simulatorView);
|
|
1235
|
+
simulatorViewAdded = true;
|
|
1236
|
+
applySimulatorBounds(simWidth);
|
|
1237
|
+
}
|
|
737
1238
|
}
|
|
738
1239
|
}
|
|
739
1240
|
function detachSimulator() {
|
|
@@ -745,6 +1246,7 @@ function createViewManager(ctx) {
|
|
|
745
1246
|
simulatorView = null;
|
|
746
1247
|
simulatorViewAdded = false;
|
|
747
1248
|
simulatorWebContentsId = null;
|
|
1249
|
+
simulatorBoundsOverride = null;
|
|
748
1250
|
}
|
|
749
1251
|
function showSimulator(simWidth) {
|
|
750
1252
|
lastSimWidth = simWidth;
|
|
@@ -777,7 +1279,7 @@ function createViewManager(ctx) {
|
|
|
777
1279
|
});
|
|
778
1280
|
applyNavigationHardening(settingsView.webContents, ctx.rendererDir);
|
|
779
1281
|
await settingsView.webContents.loadFile(
|
|
780
|
-
|
|
1282
|
+
path7.join(ctx.rendererDir, "entries/settings/index.html")
|
|
781
1283
|
);
|
|
782
1284
|
}
|
|
783
1285
|
if (!settingsViewAdded) {
|
|
@@ -814,7 +1316,7 @@ function createViewManager(ctx) {
|
|
|
814
1316
|
ctx.notify.popoverInit(popover, data);
|
|
815
1317
|
});
|
|
816
1318
|
popover.webContents.loadFile(
|
|
817
|
-
|
|
1319
|
+
path7.join(ctx.rendererDir, "entries/popover/index.html")
|
|
818
1320
|
);
|
|
819
1321
|
}
|
|
820
1322
|
function hidePopover() {
|
|
@@ -885,7 +1387,8 @@ function createViewManager(ctx) {
|
|
|
885
1387
|
return popoverView.webContents.id;
|
|
886
1388
|
},
|
|
887
1389
|
resize,
|
|
888
|
-
setVisible
|
|
1390
|
+
setVisible,
|
|
1391
|
+
setSimulatorDevtoolsBounds
|
|
889
1392
|
};
|
|
890
1393
|
}
|
|
891
1394
|
|
|
@@ -917,61 +1420,21 @@ function createWindowService(mainWindow) {
|
|
|
917
1420
|
|
|
918
1421
|
// src/main/services/projects/project-repository.ts
|
|
919
1422
|
import { app as app6 } from "electron";
|
|
920
|
-
import
|
|
921
|
-
import
|
|
922
|
-
|
|
923
|
-
// src/main/utils/logger.ts
|
|
924
|
-
var LEVEL_PRIORITY = {
|
|
925
|
-
debug: 0,
|
|
926
|
-
info: 1,
|
|
927
|
-
warn: 2,
|
|
928
|
-
error: 3
|
|
929
|
-
};
|
|
930
|
-
var LEVEL_PREFIX = {
|
|
931
|
-
debug: "[DEBUG]",
|
|
932
|
-
info: "[INFO]",
|
|
933
|
-
warn: "[WARN]",
|
|
934
|
-
error: "[ERROR]"
|
|
935
|
-
};
|
|
936
|
-
var currentLevel = "debug";
|
|
937
|
-
function shouldLog(level) {
|
|
938
|
-
return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[currentLevel];
|
|
939
|
-
}
|
|
940
|
-
function formatMessage(level, tag, message) {
|
|
941
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
942
|
-
return `${ts} ${LEVEL_PREFIX[level]} [${tag}] ${message}`;
|
|
943
|
-
}
|
|
944
|
-
function createLogger(tag) {
|
|
945
|
-
return {
|
|
946
|
-
debug(message, ...args) {
|
|
947
|
-
if (shouldLog("debug")) console.debug(formatMessage("debug", tag, message), ...args);
|
|
948
|
-
},
|
|
949
|
-
info(message, ...args) {
|
|
950
|
-
if (shouldLog("info")) console.info(formatMessage("info", tag, message), ...args);
|
|
951
|
-
},
|
|
952
|
-
warn(message, ...args) {
|
|
953
|
-
if (shouldLog("warn")) console.warn(formatMessage("warn", tag, message), ...args);
|
|
954
|
-
},
|
|
955
|
-
error(message, ...args) {
|
|
956
|
-
if (shouldLog("error")) console.error(formatMessage("error", tag, message), ...args);
|
|
957
|
-
}
|
|
958
|
-
};
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
// src/main/services/projects/project-repository.ts
|
|
962
|
-
var log = createLogger("projects");
|
|
1423
|
+
import path8 from "path";
|
|
1424
|
+
import fs4 from "fs";
|
|
1425
|
+
var log2 = createLogger("projects");
|
|
963
1426
|
function getProjectsFile() {
|
|
964
|
-
return
|
|
1427
|
+
return path8.join(app6.getPath("userData"), "dimina-projects.json");
|
|
965
1428
|
}
|
|
966
1429
|
function load() {
|
|
967
1430
|
try {
|
|
968
|
-
return JSON.parse(
|
|
1431
|
+
return JSON.parse(fs4.readFileSync(getProjectsFile(), "utf-8"));
|
|
969
1432
|
} catch {
|
|
970
1433
|
return [];
|
|
971
1434
|
}
|
|
972
1435
|
}
|
|
973
1436
|
function save(projects) {
|
|
974
|
-
|
|
1437
|
+
fs4.writeFileSync(getProjectsFile(), JSON.stringify(projects, null, 2));
|
|
975
1438
|
}
|
|
976
1439
|
function listProjects() {
|
|
977
1440
|
return load();
|
|
@@ -980,20 +1443,20 @@ function validateProjectDir(dirPath) {
|
|
|
980
1443
|
if (!dirPath) {
|
|
981
1444
|
return "\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u8DEF\u5F84\u4E3A\u7A7A\uFF0C\u8BF7\u9009\u62E9\u5305\u542B\u5C0F\u7A0B\u5E8F\u6E90\u7801\u7684\u76EE\u5F55";
|
|
982
1445
|
}
|
|
983
|
-
if (!
|
|
1446
|
+
if (!fs4.existsSync(dirPath)) {
|
|
984
1447
|
return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
|
|
985
1448
|
}
|
|
986
|
-
if (!
|
|
987
|
-
const configPath =
|
|
988
|
-
if (
|
|
1449
|
+
if (!fs4.existsSync(path8.join(dirPath, "app.json"))) {
|
|
1450
|
+
const configPath = path8.join(dirPath, "project.config.json");
|
|
1451
|
+
if (fs4.existsSync(configPath)) {
|
|
989
1452
|
try {
|
|
990
|
-
const cfg = JSON.parse(
|
|
1453
|
+
const cfg = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
991
1454
|
if (cfg.miniprogramRoot) {
|
|
992
|
-
const resolvedRoot =
|
|
1455
|
+
const resolvedRoot = path8.resolve(dirPath, cfg.miniprogramRoot);
|
|
993
1456
|
return `\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0Cproject.config.json \u4E2D\u6307\u5B9A\u4E86 miniprogramRoot: "${cfg.miniprogramRoot}"\uFF0C\u8BF7\u5BFC\u5165 ${resolvedRoot}`;
|
|
994
1457
|
}
|
|
995
1458
|
} catch (err) {
|
|
996
|
-
|
|
1459
|
+
log2.warn("Failed to parse project.config.json", err);
|
|
997
1460
|
}
|
|
998
1461
|
}
|
|
999
1462
|
return "\u8BE5\u76EE\u5F55\u7F3A\u5C11 app.json\uFF0C\u8BF7\u9009\u62E9\u5305\u542B\u5C0F\u7A0B\u5E8F\u6E90\u7801\u7684\u76EE\u5F55";
|
|
@@ -1002,15 +1465,15 @@ function validateProjectDir(dirPath) {
|
|
|
1002
1465
|
}
|
|
1003
1466
|
function addProject(dirPath) {
|
|
1004
1467
|
const projects = load();
|
|
1005
|
-
let name =
|
|
1468
|
+
let name = path8.basename(dirPath);
|
|
1006
1469
|
try {
|
|
1007
|
-
const configPath =
|
|
1008
|
-
if (
|
|
1009
|
-
const cfg = JSON.parse(
|
|
1470
|
+
const configPath = path8.join(dirPath, "project.config.json");
|
|
1471
|
+
if (fs4.existsSync(configPath)) {
|
|
1472
|
+
const cfg = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
1010
1473
|
if (cfg.projectname) name = cfg.projectname;
|
|
1011
1474
|
}
|
|
1012
1475
|
} catch (err) {
|
|
1013
|
-
|
|
1476
|
+
log2.warn("Failed to read project name from config", err);
|
|
1014
1477
|
}
|
|
1015
1478
|
const project = { name, path: dirPath, lastOpened: null };
|
|
1016
1479
|
const idx = projects.findIndex((p) => p.path === dirPath);
|
|
@@ -1043,10 +1506,10 @@ function getCompileConfig(dirPath) {
|
|
|
1043
1506
|
};
|
|
1044
1507
|
}
|
|
1045
1508
|
function getProjectPages(dirPath) {
|
|
1046
|
-
const appJsonPath =
|
|
1509
|
+
const appJsonPath = path8.join(dirPath, "app.json");
|
|
1047
1510
|
try {
|
|
1048
1511
|
const appJson = JSON.parse(
|
|
1049
|
-
|
|
1512
|
+
fs4.readFileSync(appJsonPath, "utf-8")
|
|
1050
1513
|
);
|
|
1051
1514
|
return {
|
|
1052
1515
|
pages: appJson.pages || [],
|
|
@@ -1069,8 +1532,8 @@ function getProjectSettings(projectPath) {
|
|
|
1069
1532
|
return { uploadWithSourceMap: false };
|
|
1070
1533
|
}
|
|
1071
1534
|
try {
|
|
1072
|
-
const configPath =
|
|
1073
|
-
const config = JSON.parse(
|
|
1535
|
+
const configPath = path8.join(projectPath, "project.config.json");
|
|
1536
|
+
const config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
1074
1537
|
return {
|
|
1075
1538
|
uploadWithSourceMap: !!config.setting?.uploadWithSourceMap
|
|
1076
1539
|
};
|
|
@@ -1080,17 +1543,17 @@ function getProjectSettings(projectPath) {
|
|
|
1080
1543
|
}
|
|
1081
1544
|
function updateProjectSettings(projectPath, patch) {
|
|
1082
1545
|
if (!projectPath) return;
|
|
1083
|
-
const configPath =
|
|
1546
|
+
const configPath = path8.join(projectPath, "project.config.json");
|
|
1084
1547
|
let config = {};
|
|
1085
1548
|
try {
|
|
1086
|
-
config = JSON.parse(
|
|
1549
|
+
config = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
1087
1550
|
} catch {
|
|
1088
1551
|
}
|
|
1089
1552
|
const nextSetting = {
|
|
1090
1553
|
...config.setting ?? {},
|
|
1091
1554
|
...patch.uploadWithSourceMap === void 0 ? {} : { uploadWithSourceMap: patch.uploadWithSourceMap }
|
|
1092
1555
|
};
|
|
1093
|
-
|
|
1556
|
+
fs4.writeFileSync(
|
|
1094
1557
|
configPath,
|
|
1095
1558
|
JSON.stringify({ ...config, setting: nextSetting }, null, 2)
|
|
1096
1559
|
);
|
|
@@ -1107,6 +1570,7 @@ var DEFAULT_COMPILE_CONFIG = {
|
|
|
1107
1570
|
function createWorkspaceService(ctx) {
|
|
1108
1571
|
let currentSession = null;
|
|
1109
1572
|
let currentProjectPath = "";
|
|
1573
|
+
let lastClosedProjectPath = "";
|
|
1110
1574
|
function sendStatus(status2, message, hotReload) {
|
|
1111
1575
|
ctx.notify.projectStatus(hotReload ? { status: status2, message, hotReload: true } : { status: status2, message });
|
|
1112
1576
|
}
|
|
@@ -1154,14 +1618,21 @@ function createWorkspaceService(ctx) {
|
|
|
1154
1618
|
},
|
|
1155
1619
|
validateProjectDir: async (dirPath) => provider.validateProjectDir ? provider.validateProjectDir(dirPath) : null,
|
|
1156
1620
|
async openProject(projectPath) {
|
|
1157
|
-
clearSimulatorServicewechatReferer();
|
|
1158
|
-
await disposeSession();
|
|
1159
|
-
currentProjectPath = "";
|
|
1160
1621
|
const dirError = provider.validateProjectDir ? await provider.validateProjectDir(projectPath) : null;
|
|
1161
1622
|
if (dirError) {
|
|
1162
1623
|
sendStatus("error", dirError);
|
|
1163
1624
|
return { success: false, error: dirError };
|
|
1164
1625
|
}
|
|
1626
|
+
const gate = ctx.beforeOpenProject ? await ctx.beforeOpenProject(projectPath) : void 0;
|
|
1627
|
+
if (gate && !gate.allow) {
|
|
1628
|
+
const message = gate.userMessage ?? gate.reason;
|
|
1629
|
+
sendStatus("error", message);
|
|
1630
|
+
return { success: false, error: message };
|
|
1631
|
+
}
|
|
1632
|
+
clearSimulatorServicewechatReferer();
|
|
1633
|
+
await disposeSession();
|
|
1634
|
+
currentProjectPath = "";
|
|
1635
|
+
lastClosedProjectPath = "";
|
|
1165
1636
|
sendStatus("compiling", "\u6B63\u5728\u7F16\u8BD1...");
|
|
1166
1637
|
const { compile } = loadWorkbenchSettings();
|
|
1167
1638
|
let session3;
|
|
@@ -1183,7 +1654,7 @@ function createWorkspaceService(ctx) {
|
|
|
1183
1654
|
bestEffort("updateLastOpened", () => {
|
|
1184
1655
|
if (provider.updateLastOpened) provider.updateLastOpened(projectPath);
|
|
1185
1656
|
});
|
|
1186
|
-
bestEffort("sendStatus", () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210"));
|
|
1657
|
+
bestEffort("sendStatus", () => sendStatus("ready", gate?.statusMessage ?? "\u7F16\u8BD1\u5B8C\u6210"));
|
|
1187
1658
|
bestEffort("applyReferer", () => applyRefererFromSession(session3));
|
|
1188
1659
|
return {
|
|
1189
1660
|
success: true,
|
|
@@ -1194,11 +1665,13 @@ function createWorkspaceService(ctx) {
|
|
|
1194
1665
|
async closeProject() {
|
|
1195
1666
|
clearSimulatorServicewechatReferer();
|
|
1196
1667
|
await disposeSession();
|
|
1668
|
+
if (currentProjectPath !== "") lastClosedProjectPath = currentProjectPath;
|
|
1197
1669
|
currentProjectPath = "";
|
|
1198
1670
|
ctx.views.disposeAll();
|
|
1199
1671
|
},
|
|
1200
1672
|
getSession: () => currentSession,
|
|
1201
1673
|
getProjectPath: () => currentProjectPath,
|
|
1674
|
+
getLastClosedProjectPath: () => lastClosedProjectPath,
|
|
1202
1675
|
hasActiveSession: () => currentSession !== null,
|
|
1203
1676
|
async captureThumbnail(projectPath) {
|
|
1204
1677
|
const wc = ctx.views.getSimulatorWebContents();
|
|
@@ -1238,29 +1711,29 @@ function createWorkspaceService(ctx) {
|
|
|
1238
1711
|
|
|
1239
1712
|
// src/main/services/projects/thumbnail.ts
|
|
1240
1713
|
import { createHash } from "crypto";
|
|
1241
|
-
import
|
|
1242
|
-
import
|
|
1714
|
+
import fs5 from "fs";
|
|
1715
|
+
import path9 from "path";
|
|
1243
1716
|
import { app as app7 } from "electron";
|
|
1244
1717
|
function getThumbnailDir() {
|
|
1245
|
-
return
|
|
1718
|
+
return path9.join(app7.getPath("userData"), "thumbnails");
|
|
1246
1719
|
}
|
|
1247
1720
|
function hashProjectPath(projectPath) {
|
|
1248
1721
|
return createHash("sha256").update(projectPath).digest("hex").slice(0, 16);
|
|
1249
1722
|
}
|
|
1250
1723
|
function getThumbnailPath(projectPath) {
|
|
1251
|
-
return
|
|
1724
|
+
return path9.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
|
|
1252
1725
|
}
|
|
1253
1726
|
var DATA_URL_PNG_PREFIX = "data:image/png;base64,";
|
|
1254
1727
|
function saveThumbnailFromDataUrl(projectPath, imageDataUrl) {
|
|
1255
1728
|
if (!imageDataUrl.startsWith(DATA_URL_PNG_PREFIX)) return;
|
|
1256
1729
|
const png = Buffer.from(imageDataUrl.slice(DATA_URL_PNG_PREFIX.length), "base64");
|
|
1257
|
-
|
|
1258
|
-
|
|
1730
|
+
fs5.mkdirSync(getThumbnailDir(), { recursive: true });
|
|
1731
|
+
fs5.writeFileSync(getThumbnailPath(projectPath), png);
|
|
1259
1732
|
}
|
|
1260
1733
|
function loadThumbnail(projectPath) {
|
|
1261
1734
|
const filePath = getThumbnailPath(projectPath);
|
|
1262
1735
|
try {
|
|
1263
|
-
const buf =
|
|
1736
|
+
const buf = fs5.readFileSync(filePath);
|
|
1264
1737
|
return `${DATA_URL_PNG_PREFIX}${buf.toString("base64")}`;
|
|
1265
1738
|
} catch {
|
|
1266
1739
|
return null;
|
|
@@ -1307,6 +1780,18 @@ function createSimulatorApiRegistry() {
|
|
|
1307
1780
|
}
|
|
1308
1781
|
|
|
1309
1782
|
// src/main/services/toolbar/toolbar-store.ts
|
|
1783
|
+
function toPublicAction(action) {
|
|
1784
|
+
const projected = {
|
|
1785
|
+
id: action.id,
|
|
1786
|
+
label: action.label
|
|
1787
|
+
};
|
|
1788
|
+
if (action.kind) projected.kind = action.kind;
|
|
1789
|
+
if (action.placement) projected.placement = action.placement;
|
|
1790
|
+
if (action.icon) projected.icon = action.icon;
|
|
1791
|
+
if (action.displayInitial) projected.displayInitial = action.displayInitial;
|
|
1792
|
+
if (action.avatarUrl) projected.avatarUrl = action.avatarUrl;
|
|
1793
|
+
return projected;
|
|
1794
|
+
}
|
|
1310
1795
|
function createToolbarStore() {
|
|
1311
1796
|
let actions = [];
|
|
1312
1797
|
return {
|
|
@@ -1321,7 +1806,7 @@ function createToolbarStore() {
|
|
|
1321
1806
|
actions = next.slice();
|
|
1322
1807
|
},
|
|
1323
1808
|
list() {
|
|
1324
|
-
return actions.map(
|
|
1809
|
+
return actions.map(toPublicAction);
|
|
1325
1810
|
},
|
|
1326
1811
|
getHandler(id) {
|
|
1327
1812
|
return actions.find((a) => a.id === id)?.handler;
|
|
@@ -1356,20 +1841,20 @@ function sanitizeTemplates(templates) {
|
|
|
1356
1841
|
}
|
|
1357
1842
|
|
|
1358
1843
|
// src/main/services/projects/builtin-templates.ts
|
|
1359
|
-
import
|
|
1360
|
-
var TEMPLATES_DIR =
|
|
1844
|
+
import path10 from "node:path";
|
|
1845
|
+
var TEMPLATES_DIR = path10.join(devtoolsPackageRoot, "templates");
|
|
1361
1846
|
var BUILTIN_TEMPLATES = [
|
|
1362
1847
|
{
|
|
1363
1848
|
id: "blank",
|
|
1364
1849
|
name: "Blank",
|
|
1365
1850
|
description: "\u6700\u5C0F\u9AA8\u67B6\uFF1Aapp.* + \u4E00\u4E2A index \u9875\u9762",
|
|
1366
|
-
source: { type: "directory", path:
|
|
1851
|
+
source: { type: "directory", path: path10.join(TEMPLATES_DIR, "blank") }
|
|
1367
1852
|
},
|
|
1368
1853
|
{
|
|
1369
1854
|
id: "taro-todo",
|
|
1370
1855
|
name: "Taro Todo",
|
|
1371
1856
|
description: "Taro \u7F16\u8BD1\u4EA7\u7269\u7684 Todo \u793A\u4F8B",
|
|
1372
|
-
source: { type: "directory", path:
|
|
1857
|
+
source: { type: "directory", path: path10.join(TEMPLATES_DIR, "taro-todo") }
|
|
1373
1858
|
}
|
|
1374
1859
|
];
|
|
1375
1860
|
|
|
@@ -1490,113 +1975,10 @@ function installAppMenu(ctx) {
|
|
|
1490
1975
|
}
|
|
1491
1976
|
|
|
1492
1977
|
// src/main/ipc/app.ts
|
|
1493
|
-
import
|
|
1494
|
-
|
|
1495
|
-
// src/main/utils/ipc-registry.ts
|
|
1496
|
-
import { ipcMain } from "electron";
|
|
1497
|
-
|
|
1498
|
-
// src/main/utils/ipc-schema.ts
|
|
1499
|
-
var IpcValidationError = class extends Error {
|
|
1500
|
-
channel;
|
|
1501
|
-
paths;
|
|
1502
|
-
constructor(channel, issues) {
|
|
1503
|
-
const paths = issues.map((i) => i.path.map((p) => String(p)).join(".") || "<root>");
|
|
1504
|
-
super(`Invalid IPC arguments for ${channel}`);
|
|
1505
|
-
this.name = "IpcValidationError";
|
|
1506
|
-
this.channel = channel;
|
|
1507
|
-
this.paths = paths;
|
|
1508
|
-
}
|
|
1509
|
-
};
|
|
1510
|
-
function validate(channel, schema, args) {
|
|
1511
|
-
const r = schema.safeParse(args);
|
|
1512
|
-
if (!r.success) {
|
|
1513
|
-
console.warn("[ipc] schema reject", channel, r.error.issues);
|
|
1514
|
-
throw new IpcValidationError(channel, r.error.issues);
|
|
1515
|
-
}
|
|
1516
|
-
return r.data;
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
// src/main/utils/ipc-registry.ts
|
|
1520
|
-
var log2 = createLogger("ipc");
|
|
1521
|
-
function reportListenerError(channel, err) {
|
|
1522
|
-
if (err instanceof IpcValidationError) {
|
|
1523
|
-
log2.warn(`schema reject on '${channel}' at [${err.paths.join(", ")}]`);
|
|
1524
|
-
return;
|
|
1525
|
-
}
|
|
1526
|
-
if (err instanceof Error) {
|
|
1527
|
-
log2.error(`listener threw on '${channel}': ${err.message}`, err.stack);
|
|
1528
|
-
return;
|
|
1529
|
-
}
|
|
1530
|
-
log2.error(`listener threw on '${channel}'`, err);
|
|
1531
|
-
}
|
|
1532
|
-
function isThenable(v) {
|
|
1533
|
-
return v != null && (typeof v === "object" || typeof v === "function") && typeof v.then === "function";
|
|
1534
|
-
}
|
|
1535
|
-
function summarizeSender(sender) {
|
|
1536
|
-
if (sender.isDestroyed()) return "<destroyed>";
|
|
1537
|
-
const url = sender.getURL();
|
|
1538
|
-
return `id=${sender.id} ${url.slice(0, 120)}`;
|
|
1539
|
-
}
|
|
1540
|
-
var IpcRegistry = class {
|
|
1541
|
-
constructor(policy) {
|
|
1542
|
-
this.policy = policy;
|
|
1543
|
-
}
|
|
1544
|
-
policy;
|
|
1545
|
-
registry = new DisposableRegistry();
|
|
1546
|
-
handle(channel, fn) {
|
|
1547
|
-
const policy = this.policy;
|
|
1548
|
-
const guarded = policy ? async (event, ...args) => {
|
|
1549
|
-
const sender = event.sender;
|
|
1550
|
-
if (!policy(sender)) {
|
|
1551
|
-
console.warn(
|
|
1552
|
-
`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`
|
|
1553
|
-
);
|
|
1554
|
-
throw new Error(`IPC sender rejected for channel ${channel}`);
|
|
1555
|
-
}
|
|
1556
|
-
return fn(event, ...args);
|
|
1557
|
-
} : fn;
|
|
1558
|
-
ipcMain.handle(channel, guarded);
|
|
1559
|
-
this.registry.add(() => ipcMain.removeHandler(channel));
|
|
1560
|
-
return this;
|
|
1561
|
-
}
|
|
1562
|
-
on(channel, fn) {
|
|
1563
|
-
const policy = this.policy;
|
|
1564
|
-
const raw = fn;
|
|
1565
|
-
const safeInvoke = (event, args) => {
|
|
1566
|
-
try {
|
|
1567
|
-
const ret = raw(event, ...args);
|
|
1568
|
-
if (isThenable(ret)) {
|
|
1569
|
-
Promise.resolve(ret).catch((err) => reportListenerError(channel, err));
|
|
1570
|
-
}
|
|
1571
|
-
} catch (err) {
|
|
1572
|
-
reportListenerError(channel, err);
|
|
1573
|
-
}
|
|
1574
|
-
};
|
|
1575
|
-
const guarded = policy ? (event, ...args) => {
|
|
1576
|
-
const sender = event.sender;
|
|
1577
|
-
if (!policy(sender)) {
|
|
1578
|
-
console.warn(
|
|
1579
|
-
`[ipc] sender rejected for channel '${channel}' (${summarizeSender(sender)})`
|
|
1580
|
-
);
|
|
1581
|
-
return;
|
|
1582
|
-
}
|
|
1583
|
-
safeInvoke(event, args);
|
|
1584
|
-
} : (event, ...args) => safeInvoke(event, args);
|
|
1585
|
-
ipcMain.on(channel, guarded);
|
|
1586
|
-
this.registry.add(() => {
|
|
1587
|
-
ipcMain.removeListener(channel, guarded);
|
|
1588
|
-
});
|
|
1589
|
-
return this;
|
|
1590
|
-
}
|
|
1591
|
-
dispose() {
|
|
1592
|
-
return this.registry.dispose();
|
|
1593
|
-
}
|
|
1594
|
-
};
|
|
1595
|
-
|
|
1596
|
-
// src/main/ipc/app.ts
|
|
1978
|
+
import fs6 from "fs";
|
|
1597
1979
|
function registerAppIpc(ctx) {
|
|
1598
1980
|
return new IpcRegistry(ctx.senderPolicy).handle(AppChannel.GetPreloadPath, () => {
|
|
1599
|
-
return `file://${
|
|
1981
|
+
return `file://${fs6.realpathSync(ctx.preloadPath)}`;
|
|
1600
1982
|
}).handle(AppChannel.GetBranding, async () => {
|
|
1601
1983
|
if (ctx.brandingProvider) return ctx.brandingProvider();
|
|
1602
1984
|
return { appName: ctx.appName };
|
|
@@ -1606,71 +1988,80 @@ function registerAppIpc(ctx) {
|
|
|
1606
1988
|
}
|
|
1607
1989
|
|
|
1608
1990
|
// src/shared/ipc-schemas.ts
|
|
1609
|
-
import { z } from "zod";
|
|
1610
|
-
var AbsolutePath =
|
|
1991
|
+
import { z as z2 } from "zod";
|
|
1992
|
+
var AbsolutePath = z2.string().min(1).refine(
|
|
1611
1993
|
(p) => p.startsWith("/") || /^[a-zA-Z]:\\/.test(p) || /^[a-zA-Z]:\//.test(p),
|
|
1612
1994
|
"must be an absolute path"
|
|
1613
1995
|
);
|
|
1614
|
-
var PanelEvalSchema =
|
|
1615
|
-
var ProjectsAddSchema =
|
|
1616
|
-
var ProjectOpenSchema =
|
|
1617
|
-
var CompileConfigShape =
|
|
1618
|
-
startPage:
|
|
1619
|
-
scene:
|
|
1620
|
-
queryParams:
|
|
1996
|
+
var PanelEvalSchema = z2.tuple([z2.string().min(1).max(5e4)]);
|
|
1997
|
+
var ProjectsAddSchema = z2.tuple([AbsolutePath]);
|
|
1998
|
+
var ProjectOpenSchema = z2.tuple([AbsolutePath]);
|
|
1999
|
+
var CompileConfigShape = z2.looseObject({
|
|
2000
|
+
startPage: z2.string().optional(),
|
|
2001
|
+
scene: z2.number().optional(),
|
|
2002
|
+
queryParams: z2.array(z2.looseObject({ key: z2.string(), value: z2.string() })).optional()
|
|
1621
2003
|
});
|
|
1622
|
-
var ProjectSaveCompileConfigSchema =
|
|
2004
|
+
var ProjectSaveCompileConfigSchema = z2.tuple([
|
|
1623
2005
|
AbsolutePath,
|
|
1624
2006
|
CompileConfigShape
|
|
1625
2007
|
]);
|
|
1626
|
-
var PopoverShowSchema =
|
|
1627
|
-
var SimWidth =
|
|
1628
|
-
var SimulatorAttachSchema =
|
|
1629
|
-
|
|
2008
|
+
var PopoverShowSchema = z2.tuple([z2.looseObject({})]);
|
|
2009
|
+
var SimWidth = z2.number().int().min(100).max(2e3);
|
|
2010
|
+
var SimulatorAttachSchema = z2.tuple([
|
|
2011
|
+
z2.number().int().positive(),
|
|
1630
2012
|
SimWidth
|
|
1631
2013
|
]);
|
|
1632
|
-
var SimulatorResizeSchema =
|
|
1633
|
-
var
|
|
1634
|
-
var
|
|
1635
|
-
|
|
1636
|
-
|
|
2014
|
+
var SimulatorResizeSchema = z2.tuple([SimWidth]);
|
|
2015
|
+
var NonNegInt = z2.number().int().min(0).max(1e5);
|
|
2016
|
+
var ViewBoundsSchema = z2.tuple([
|
|
2017
|
+
z2.object({
|
|
2018
|
+
x: NonNegInt,
|
|
2019
|
+
y: NonNegInt,
|
|
2020
|
+
width: NonNegInt,
|
|
2021
|
+
height: NonNegInt
|
|
2022
|
+
})
|
|
2023
|
+
]);
|
|
2024
|
+
var SimulatorSetVisibleSchema = z2.tuple([z2.boolean(), SimWidth]);
|
|
2025
|
+
var SimulatorCustomApiInvokeSchema = z2.tuple([
|
|
2026
|
+
z2.string().min(1).max(256),
|
|
2027
|
+
z2.unknown()
|
|
1637
2028
|
]);
|
|
1638
|
-
var ToolbarInvokeSchema =
|
|
1639
|
-
var ProjectGetPagesSchema =
|
|
1640
|
-
var ProjectGetCompileConfigSchema =
|
|
1641
|
-
var ProjectsRemoveSchema =
|
|
1642
|
-
var WorkbenchSettingsSaveSchema =
|
|
1643
|
-
|
|
1644
|
-
cdp:
|
|
1645
|
-
enabled:
|
|
1646
|
-
port:
|
|
2029
|
+
var ToolbarInvokeSchema = z2.tuple([z2.string().min(1).max(256)]);
|
|
2030
|
+
var ProjectGetPagesSchema = z2.tuple([AbsolutePath]);
|
|
2031
|
+
var ProjectGetCompileConfigSchema = z2.tuple([AbsolutePath]);
|
|
2032
|
+
var ProjectsRemoveSchema = z2.tuple([AbsolutePath]);
|
|
2033
|
+
var WorkbenchSettingsSaveSchema = z2.tuple([
|
|
2034
|
+
z2.looseObject({
|
|
2035
|
+
cdp: z2.looseObject({
|
|
2036
|
+
enabled: z2.boolean(),
|
|
2037
|
+
port: z2.number().int().min(0).max(65535)
|
|
1647
2038
|
}),
|
|
1648
|
-
mcp:
|
|
1649
|
-
enabled:
|
|
1650
|
-
port:
|
|
2039
|
+
mcp: z2.looseObject({
|
|
2040
|
+
enabled: z2.boolean(),
|
|
2041
|
+
port: z2.number().int().min(0).max(65535)
|
|
1651
2042
|
}),
|
|
1652
|
-
compile:
|
|
1653
|
-
watch:
|
|
2043
|
+
compile: z2.looseObject({
|
|
2044
|
+
watch: z2.boolean()
|
|
1654
2045
|
}),
|
|
1655
|
-
theme:
|
|
1656
|
-
lastCreateBaseDir:
|
|
2046
|
+
theme: z2.enum(["system", "light", "dark"]),
|
|
2047
|
+
lastCreateBaseDir: z2.union([z2.string(), z2.null()])
|
|
1657
2048
|
})
|
|
1658
2049
|
]);
|
|
1659
|
-
var WorkbenchSettingsSetThemeSchema =
|
|
1660
|
-
|
|
2050
|
+
var WorkbenchSettingsSetThemeSchema = z2.tuple([
|
|
2051
|
+
z2.enum(["system", "light", "dark"])
|
|
1661
2052
|
]);
|
|
1662
|
-
var WorkbenchSettingsSetVisibleSchema =
|
|
1663
|
-
var SettingsSetVisibleSchema =
|
|
1664
|
-
var SettingsConfigChangedSchema =
|
|
1665
|
-
var PopoverRelaunchSchema =
|
|
1666
|
-
var SettingsProjectSettingsChangedSchema =
|
|
1667
|
-
|
|
1668
|
-
uploadWithSourceMap:
|
|
2053
|
+
var WorkbenchSettingsSetVisibleSchema = z2.tuple([z2.boolean()]);
|
|
2054
|
+
var SettingsSetVisibleSchema = z2.tuple([z2.boolean()]);
|
|
2055
|
+
var SettingsConfigChangedSchema = z2.tuple([CompileConfigShape]);
|
|
2056
|
+
var PopoverRelaunchSchema = z2.tuple([CompileConfigShape]);
|
|
2057
|
+
var SettingsProjectSettingsChangedSchema = z2.tuple([
|
|
2058
|
+
z2.looseObject({
|
|
2059
|
+
uploadWithSourceMap: z2.boolean().optional()
|
|
1669
2060
|
})
|
|
1670
2061
|
]);
|
|
1671
|
-
var PanelSelectSchema =
|
|
1672
|
-
var ProjectCaptureThumbnailSchema =
|
|
1673
|
-
var ProjectGetThumbnailSchema =
|
|
2062
|
+
var PanelSelectSchema = z2.tuple([z2.string().min(1).max(200)]);
|
|
2063
|
+
var ProjectCaptureThumbnailSchema = z2.tuple([AbsolutePath]);
|
|
2064
|
+
var ProjectGetThumbnailSchema = z2.tuple([AbsolutePath]);
|
|
1674
2065
|
|
|
1675
2066
|
// src/main/ipc/simulator.ts
|
|
1676
2067
|
function registerSimulatorIpc(ctx) {
|
|
@@ -1743,11 +2134,11 @@ var popoverModule = {
|
|
|
1743
2134
|
|
|
1744
2135
|
// src/main/ipc/projects.ts
|
|
1745
2136
|
import { app as app8, dialog } from "electron";
|
|
1746
|
-
import
|
|
2137
|
+
import path12 from "path";
|
|
1747
2138
|
|
|
1748
2139
|
// src/main/services/projects/create-project-service.ts
|
|
1749
|
-
import
|
|
1750
|
-
import
|
|
2140
|
+
import fs7 from "node:fs";
|
|
2141
|
+
import path11 from "node:path";
|
|
1751
2142
|
var DEFAULT_TEMPLATE_ID = "blank";
|
|
1752
2143
|
async function createProject(input, ctx) {
|
|
1753
2144
|
const name = (input.name ?? "").trim();
|
|
@@ -1758,12 +2149,12 @@ async function createProject(input, ctx) {
|
|
|
1758
2149
|
if (!target || typeof target !== "string") {
|
|
1759
2150
|
throw new Error("Project path is required");
|
|
1760
2151
|
}
|
|
1761
|
-
if (
|
|
1762
|
-
const stat =
|
|
2152
|
+
if (fs7.existsSync(target)) {
|
|
2153
|
+
const stat = fs7.statSync(target);
|
|
1763
2154
|
if (!stat.isDirectory()) {
|
|
1764
2155
|
throw new Error(`Project path exists and is not a directory: ${target}`);
|
|
1765
2156
|
}
|
|
1766
|
-
const entries =
|
|
2157
|
+
const entries = fs7.readdirSync(target);
|
|
1767
2158
|
if (entries.length > 0) {
|
|
1768
2159
|
throw new Error(
|
|
1769
2160
|
`Project path is not empty (refusing to overwrite): ${target}`
|
|
@@ -1780,31 +2171,31 @@ async function createProject(input, ctx) {
|
|
|
1780
2171
|
`Template '${templateId}' has neither a source directory nor a generate function`
|
|
1781
2172
|
);
|
|
1782
2173
|
}
|
|
1783
|
-
|
|
2174
|
+
fs7.mkdirSync(target, { recursive: true });
|
|
1784
2175
|
if (template.generate) {
|
|
1785
2176
|
await template.generate(target, { name });
|
|
1786
2177
|
} else if (template.source) {
|
|
1787
|
-
if (!
|
|
2178
|
+
if (!fs7.existsSync(template.source.path)) {
|
|
1788
2179
|
throw new Error(
|
|
1789
2180
|
`Template source missing on disk: ${template.source.path}`
|
|
1790
2181
|
);
|
|
1791
2182
|
}
|
|
1792
|
-
|
|
2183
|
+
fs7.cpSync(template.source.path, target, {
|
|
1793
2184
|
recursive: true,
|
|
1794
2185
|
force: true
|
|
1795
2186
|
});
|
|
1796
2187
|
}
|
|
1797
|
-
const cfgPath =
|
|
2188
|
+
const cfgPath = path11.join(target, "project.config.json");
|
|
1798
2189
|
let cfg = {};
|
|
1799
|
-
if (
|
|
2190
|
+
if (fs7.existsSync(cfgPath)) {
|
|
1800
2191
|
try {
|
|
1801
|
-
cfg = JSON.parse(
|
|
2192
|
+
cfg = JSON.parse(fs7.readFileSync(cfgPath, "utf-8"));
|
|
1802
2193
|
} catch {
|
|
1803
2194
|
cfg = {};
|
|
1804
2195
|
}
|
|
1805
2196
|
}
|
|
1806
2197
|
cfg.projectname = name;
|
|
1807
|
-
|
|
2198
|
+
fs7.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
|
|
1808
2199
|
const created = await ctx.projectsProvider.addProject(target);
|
|
1809
2200
|
return created;
|
|
1810
2201
|
}
|
|
@@ -1880,7 +2271,7 @@ function registerProjectsIpc(ctx) {
|
|
|
1880
2271
|
}
|
|
1881
2272
|
try {
|
|
1882
2273
|
const settings = loadWorkbenchSettings();
|
|
1883
|
-
const newBase =
|
|
2274
|
+
const newBase = path12.dirname(input.path);
|
|
1884
2275
|
if (newBase && settings.lastCreateBaseDir !== newBase) {
|
|
1885
2276
|
saveWorkbenchSettings({ ...settings, lastCreateBaseDir: newBase });
|
|
1886
2277
|
}
|
|
@@ -2074,6 +2465,18 @@ function registerToolbarIpc(ctx) {
|
|
|
2074
2465
|
});
|
|
2075
2466
|
}
|
|
2076
2467
|
|
|
2468
|
+
// src/main/ipc/views.ts
|
|
2469
|
+
function registerViewsIpc(ctx) {
|
|
2470
|
+
return new IpcRegistry(ctx.senderPolicy).handle(ViewChannel.SimulatorDevtoolsBounds, (_event, ...args) => {
|
|
2471
|
+
const [bounds] = validate(
|
|
2472
|
+
ViewChannel.SimulatorDevtoolsBounds,
|
|
2473
|
+
ViewBoundsSchema,
|
|
2474
|
+
args
|
|
2475
|
+
);
|
|
2476
|
+
ctx.views.setSimulatorDevtoolsBounds(bounds);
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2077
2480
|
// src/main/ipc/simulator-module.ts
|
|
2078
2481
|
var simulatorModule = {
|
|
2079
2482
|
setup: (ctx) => {
|
|
@@ -2081,6 +2484,7 @@ var simulatorModule = {
|
|
|
2081
2484
|
reg.add(registerSimulatorIpc(ctx));
|
|
2082
2485
|
reg.add(registerPanelsIpc(ctx));
|
|
2083
2486
|
reg.add(registerToolbarIpc(ctx));
|
|
2487
|
+
reg.add(registerViewsIpc(ctx));
|
|
2084
2488
|
return reg;
|
|
2085
2489
|
}
|
|
2086
2490
|
};
|
|
@@ -2774,7 +3178,7 @@ function getClient(kind) {
|
|
|
2774
3178
|
}
|
|
2775
3179
|
|
|
2776
3180
|
// src/main/services/mcp/tool-registry.ts
|
|
2777
|
-
import { z as
|
|
3181
|
+
import { z as z3 } from "zod";
|
|
2778
3182
|
function registerCommonTargetTools(server, kind) {
|
|
2779
3183
|
const descriptions = kind === "simulator" ? {
|
|
2780
3184
|
screenshot: "Take a screenshot of the simulator webview",
|
|
@@ -2795,9 +3199,9 @@ function registerCommonTargetTools(server, kind) {
|
|
|
2795
3199
|
return { content: [{ type: "image", data, mimeType: "image/png" }] };
|
|
2796
3200
|
});
|
|
2797
3201
|
server.tool(`${kind}_console_logs`, descriptions.consoleLogs, {
|
|
2798
|
-
limit:
|
|
2799
|
-
level:
|
|
2800
|
-
sinceTimestamp:
|
|
3202
|
+
limit: z3.number().optional().default(50).describe("Maximum number of log entries"),
|
|
3203
|
+
level: z3.enum(["error", "warning", "warn", "info", "log", "debug"]).optional().describe("Only return entries with this level"),
|
|
3204
|
+
sinceTimestamp: z3.string().optional().describe("ISO timestamp; only return entries at or after this time")
|
|
2801
3205
|
}, async ({ limit, level, sinceTimestamp }) => {
|
|
2802
3206
|
getClient(kind);
|
|
2803
3207
|
let entries = getTargetState(kind).consoleLogs;
|
|
@@ -2812,7 +3216,7 @@ function registerCommonTargetTools(server, kind) {
|
|
|
2812
3216
|
});
|
|
2813
3217
|
if (kind !== "workbench") {
|
|
2814
3218
|
server.tool(`${kind}_evaluate`, descriptions.evaluate, {
|
|
2815
|
-
expression:
|
|
3219
|
+
expression: z3.string().describe("JavaScript expression to evaluate")
|
|
2816
3220
|
}, async ({ expression }) => {
|
|
2817
3221
|
const c = getClient(kind);
|
|
2818
3222
|
const result = await c.Runtime.evaluate({ expression, returnByValue: true, awaitPromise: true });
|
|
@@ -2825,15 +3229,15 @@ ${JSON.stringify(result.exceptionDetails, null, 2)}` }], isError: true };
|
|
|
2825
3229
|
});
|
|
2826
3230
|
}
|
|
2827
3231
|
server.tool(`${kind}_get_dom`, descriptions.getDom, {
|
|
2828
|
-
depth:
|
|
3232
|
+
depth: z3.number().optional().default(3).describe("Maximum depth")
|
|
2829
3233
|
}, async ({ depth }) => {
|
|
2830
3234
|
const c = getClient(kind);
|
|
2831
3235
|
const { root } = await c.DOM.getDocument({ depth });
|
|
2832
3236
|
return { content: [{ type: "text", text: JSON.stringify(root, null, 2) }] };
|
|
2833
3237
|
});
|
|
2834
3238
|
server.tool(`${kind}_network_log`, descriptions.networkLog, {
|
|
2835
|
-
limit:
|
|
2836
|
-
minStatus:
|
|
3239
|
+
limit: z3.number().optional().default(20).describe("Maximum number of entries"),
|
|
3240
|
+
minStatus: z3.number().optional().describe("Only return entries with status >= minStatus (status 0 = failed before response is included when minStatus >= 400)")
|
|
2837
3241
|
}, async ({ limit, minStatus }) => {
|
|
2838
3242
|
getClient(kind);
|
|
2839
3243
|
let entries = getTargetState(kind).networkRequests;
|
|
@@ -3076,11 +3480,11 @@ function registerContextTools(server) {
|
|
|
3076
3480
|
}
|
|
3077
3481
|
|
|
3078
3482
|
// src/main/services/mcp/tools/simulator-tools.ts
|
|
3079
|
-
import { z as
|
|
3483
|
+
import { z as z4 } from "zod";
|
|
3080
3484
|
function registerSimulatorTools(server) {
|
|
3081
3485
|
server.tool("simulator_navigate", "Navigate the simulator to a URL, or reload the current page", {
|
|
3082
|
-
url:
|
|
3083
|
-
reload:
|
|
3486
|
+
url: z4.string().optional().describe("URL to navigate to"),
|
|
3487
|
+
reload: z4.boolean().optional().describe("If true, reload current page instead of navigating")
|
|
3084
3488
|
}, async ({ url, reload }) => {
|
|
3085
3489
|
const c = getClient("simulator");
|
|
3086
3490
|
if (reload) {
|
|
@@ -3095,15 +3499,15 @@ function registerSimulatorTools(server) {
|
|
|
3095
3499
|
return { content: [{ type: "text", text: "Error: either url or reload is required" }], isError: true };
|
|
3096
3500
|
});
|
|
3097
3501
|
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.", {
|
|
3098
|
-
action:
|
|
3099
|
-
x:
|
|
3100
|
-
y:
|
|
3101
|
-
selector:
|
|
3102
|
-
nth:
|
|
3103
|
-
text:
|
|
3104
|
-
key:
|
|
3105
|
-
deltaX:
|
|
3106
|
-
deltaY:
|
|
3502
|
+
action: z4.enum(["tap_coord", "tap_selector", "type", "scroll", "key"]).describe("Input action to dispatch"),
|
|
3503
|
+
x: z4.number().optional().describe("Viewport x coordinate for `tap_coord` or `scroll`"),
|
|
3504
|
+
y: z4.number().optional().describe("Viewport y coordinate for `tap_coord` or `scroll`"),
|
|
3505
|
+
selector: z4.string().optional().describe("CSS selector for `tap_selector` or `type`"),
|
|
3506
|
+
nth: z4.number().int().optional().describe("Zero-based index when selector matches multiple elements; defaults to 0"),
|
|
3507
|
+
text: z4.string().optional().describe("Text to insert for `type`"),
|
|
3508
|
+
key: z4.string().optional().describe("Best-effort keyboard key name for `key`; common keys such as Enter, Escape, Tab, Arrow* and Space work best"),
|
|
3509
|
+
deltaX: z4.number().optional().describe("Horizontal wheel delta for `scroll`"),
|
|
3510
|
+
deltaY: z4.number().optional().describe("Vertical wheel delta for `scroll`")
|
|
3107
3511
|
}, async ({ action, x, y, selector, nth, text, key, deltaX, deltaY }) => {
|
|
3108
3512
|
const c = getClient("simulator");
|
|
3109
3513
|
const err = (msg) => ({ content: [{ type: "text", text: `Error: ${msg}` }], isError: true });
|
|
@@ -3558,29 +3962,28 @@ async function clearElementInspection() {
|
|
|
3558
3962
|
}
|
|
3559
3963
|
|
|
3560
3964
|
// src/main/services/simulator-temp-files/store.ts
|
|
3561
|
-
function registerTempFile(store,
|
|
3965
|
+
function registerTempFile(store, path18, mime, bytes) {
|
|
3562
3966
|
const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(new Uint8Array(bytes));
|
|
3563
|
-
store.delete(
|
|
3564
|
-
store.set(
|
|
3967
|
+
store.delete(path18);
|
|
3968
|
+
store.set(path18, { bytes: buf, mime });
|
|
3565
3969
|
}
|
|
3566
|
-
function revokeTempFile(store,
|
|
3567
|
-
store.delete(
|
|
3970
|
+
function revokeTempFile(store, path18) {
|
|
3971
|
+
store.delete(path18);
|
|
3568
3972
|
}
|
|
3569
3973
|
function revokeAllTempFiles(store) {
|
|
3570
3974
|
store.clear();
|
|
3571
3975
|
}
|
|
3572
3976
|
|
|
3573
|
-
// src/
|
|
3574
|
-
|
|
3575
|
-
|
|
3977
|
+
// src/shared/vpath.ts
|
|
3978
|
+
import os from "node:os";
|
|
3979
|
+
import path13 from "node:path";
|
|
3576
3980
|
var DIFILE_PREFIX = "difile://";
|
|
3577
3981
|
function sandboxBase() {
|
|
3578
3982
|
const env = typeof process !== "undefined" && process.env && process.env.DIMINA_HOME || "";
|
|
3579
3983
|
if (env) {
|
|
3580
|
-
return
|
|
3984
|
+
return path13.join(env, "files");
|
|
3581
3985
|
}
|
|
3582
|
-
|
|
3583
|
-
return _path.join(home, ".dimina", "files");
|
|
3986
|
+
return path13.join(os.homedir(), ".dimina", "files");
|
|
3584
3987
|
}
|
|
3585
3988
|
function resolveVPath(url) {
|
|
3586
3989
|
if (typeof url !== "string") return null;
|
|
@@ -3614,15 +4017,15 @@ function resolveVPath(url) {
|
|
|
3614
4017
|
return { kind, writable };
|
|
3615
4018
|
}
|
|
3616
4019
|
const base = sandboxBase();
|
|
3617
|
-
const joined =
|
|
3618
|
-
const normalized =
|
|
3619
|
-
if (normalized !== base && !normalized.startsWith(base +
|
|
4020
|
+
const joined = path13.join(base, decoded);
|
|
4021
|
+
const normalized = path13.normalize(joined);
|
|
4022
|
+
if (normalized !== base && !normalized.startsWith(base + path13.sep)) return null;
|
|
3620
4023
|
return { kind, writable, realPath: normalized };
|
|
3621
4024
|
}
|
|
3622
4025
|
|
|
3623
4026
|
// src/main/services/simulator-temp-files/disk.ts
|
|
3624
|
-
import
|
|
3625
|
-
import
|
|
4027
|
+
import fs8 from "node:fs/promises";
|
|
4028
|
+
import path14 from "node:path";
|
|
3626
4029
|
var EXT_MIME = {
|
|
3627
4030
|
".txt": "text/plain",
|
|
3628
4031
|
".log": "text/plain",
|
|
@@ -3687,7 +4090,7 @@ function sniffMime(head) {
|
|
|
3687
4090
|
return null;
|
|
3688
4091
|
}
|
|
3689
4092
|
function extMime(realPath) {
|
|
3690
|
-
const ext =
|
|
4093
|
+
const ext = path14.extname(realPath).toLowerCase();
|
|
3691
4094
|
if (!ext) return null;
|
|
3692
4095
|
return EXT_MIME[ext] ?? null;
|
|
3693
4096
|
}
|
|
@@ -3702,7 +4105,7 @@ function etagOf(mtimeMs, size) {
|
|
|
3702
4105
|
return `W/"${Math.floor(mtimeMs)}-${size}"`;
|
|
3703
4106
|
}
|
|
3704
4107
|
async function readDiskFile(realPath, opts) {
|
|
3705
|
-
const handle = await
|
|
4108
|
+
const handle = await fs8.open(realPath, "r");
|
|
3706
4109
|
try {
|
|
3707
4110
|
const st = await handle.stat();
|
|
3708
4111
|
const totalSize = st.size;
|
|
@@ -3757,15 +4160,15 @@ async function readDiskFile(realPath, opts) {
|
|
|
3757
4160
|
}
|
|
3758
4161
|
async function writeDiskFile(realPath, bytes) {
|
|
3759
4162
|
const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(new Uint8Array(bytes));
|
|
3760
|
-
await
|
|
3761
|
-
await
|
|
4163
|
+
await fs8.mkdir(path14.dirname(realPath), { recursive: true });
|
|
4164
|
+
await fs8.writeFile(realPath, buf);
|
|
3762
4165
|
}
|
|
3763
4166
|
async function readDiskDir(realPath) {
|
|
3764
|
-
const entries = await
|
|
4167
|
+
const entries = await fs8.readdir(realPath);
|
|
3765
4168
|
return entries;
|
|
3766
4169
|
}
|
|
3767
4170
|
async function statDiskFile(realPath) {
|
|
3768
|
-
const st = await
|
|
4171
|
+
const st = await fs8.stat(realPath);
|
|
3769
4172
|
return {
|
|
3770
4173
|
size: st.size,
|
|
3771
4174
|
mtime: Math.floor(st.mtimeMs),
|
|
@@ -3898,29 +4301,29 @@ async function handleDifileRequest(ctx, req) {
|
|
|
3898
4301
|
}
|
|
3899
4302
|
|
|
3900
4303
|
// src/main/services/simulator-temp-files/fs-channels.ts
|
|
3901
|
-
import
|
|
3902
|
-
import
|
|
4304
|
+
import fs9 from "node:fs/promises";
|
|
4305
|
+
import path15 from "node:path";
|
|
3903
4306
|
async function enforceSandbox(realPath) {
|
|
3904
4307
|
if (typeof realPath !== "string" || realPath.length === 0) {
|
|
3905
4308
|
throw new Error("sandbox: realPath must be a non-empty string");
|
|
3906
4309
|
}
|
|
3907
4310
|
const base = sandboxBase();
|
|
3908
|
-
const baseReal = await
|
|
3909
|
-
const normalized =
|
|
3910
|
-
if (normalized !== base && !normalized.startsWith(base +
|
|
4311
|
+
const baseReal = await fs9.realpath(base).catch(() => base);
|
|
4312
|
+
const normalized = path15.normalize(realPath);
|
|
4313
|
+
if (normalized !== base && !normalized.startsWith(base + path15.sep)) {
|
|
3911
4314
|
throw new Error("sandbox: realPath escapes the user-data base");
|
|
3912
4315
|
}
|
|
3913
4316
|
let probe = normalized;
|
|
3914
|
-
while (probe !==
|
|
4317
|
+
while (probe !== path15.parse(probe).root) {
|
|
3915
4318
|
try {
|
|
3916
|
-
const resolved = await
|
|
3917
|
-
if (resolved !== baseReal && !resolved.startsWith(baseReal +
|
|
4319
|
+
const resolved = await fs9.realpath(probe);
|
|
4320
|
+
if (resolved !== baseReal && !resolved.startsWith(baseReal + path15.sep)) {
|
|
3918
4321
|
throw new Error("sandbox: realPath escapes the user-data base via symlink");
|
|
3919
4322
|
}
|
|
3920
4323
|
break;
|
|
3921
4324
|
} catch (err) {
|
|
3922
4325
|
if (err.code === "ENOENT") {
|
|
3923
|
-
probe =
|
|
4326
|
+
probe = path15.dirname(probe);
|
|
3924
4327
|
continue;
|
|
3925
4328
|
}
|
|
3926
4329
|
throw err;
|
|
@@ -3953,12 +4356,12 @@ async function handleFsReaddir(req) {
|
|
|
3953
4356
|
}
|
|
3954
4357
|
async function handleFsUnlink(req) {
|
|
3955
4358
|
const safe = await enforceSandbox(req.realPath);
|
|
3956
|
-
await
|
|
4359
|
+
await fs9.unlink(safe);
|
|
3957
4360
|
return { ok: true };
|
|
3958
4361
|
}
|
|
3959
4362
|
async function handleFsMkdir(req) {
|
|
3960
4363
|
const safe = await enforceSandbox(req.realPath);
|
|
3961
|
-
await
|
|
4364
|
+
await fs9.mkdir(safe, { recursive: !!req.recursive });
|
|
3962
4365
|
return { ok: true };
|
|
3963
4366
|
}
|
|
3964
4367
|
|
|
@@ -3976,10 +4379,10 @@ function setupSimulatorTempFiles(simSession) {
|
|
|
3976
4379
|
const store = /* @__PURE__ */ new Map();
|
|
3977
4380
|
const pendingWaiters = /* @__PURE__ */ new Map();
|
|
3978
4381
|
let disposed = false;
|
|
3979
|
-
function drainWaiters(
|
|
3980
|
-
const list = pendingWaiters.get(
|
|
4382
|
+
function drainWaiters(path18) {
|
|
4383
|
+
const list = pendingWaiters.get(path18);
|
|
3981
4384
|
if (!list) return;
|
|
3982
|
-
pendingWaiters.delete(
|
|
4385
|
+
pendingWaiters.delete(path18);
|
|
3983
4386
|
for (const fn of list) fn();
|
|
3984
4387
|
}
|
|
3985
4388
|
function drainAllWaiters() {
|
|
@@ -3991,15 +4394,15 @@ function setupSimulatorTempFiles(simSession) {
|
|
|
3991
4394
|
const registry = new IpcRegistry(simulatorOnlyPolicy);
|
|
3992
4395
|
registry.on("simulator:temp-file:write", (_event, payload) => {
|
|
3993
4396
|
if (disposed) return;
|
|
3994
|
-
const { path:
|
|
3995
|
-
registerTempFile(store,
|
|
4397
|
+
const { path: path18, mime, bytes } = payload;
|
|
4398
|
+
registerTempFile(store, path18, mime, bytes);
|
|
3996
4399
|
enforceStoreCap(store);
|
|
3997
|
-
drainWaiters(
|
|
4400
|
+
drainWaiters(path18);
|
|
3998
4401
|
});
|
|
3999
4402
|
registry.on("simulator:temp-file:revoke", (_event, payload) => {
|
|
4000
4403
|
if (disposed) return;
|
|
4001
|
-
const { path:
|
|
4002
|
-
revokeTempFile(store,
|
|
4404
|
+
const { path: path18 } = payload;
|
|
4405
|
+
revokeTempFile(store, path18);
|
|
4003
4406
|
});
|
|
4004
4407
|
registry.on("simulator:temp-file:revoke-all", () => {
|
|
4005
4408
|
if (disposed) return;
|
|
@@ -4087,6 +4490,16 @@ var UpdateManager = class {
|
|
|
4087
4490
|
getCurrentVersion;
|
|
4088
4491
|
latestUpdate = null;
|
|
4089
4492
|
downloadedPath = null;
|
|
4493
|
+
/**
|
|
4494
|
+
* Whether this instance has already emitted `Available` once. The periodic
|
|
4495
|
+
* check fires forever (interval default 1h), and downstream UI (qdmp shell
|
|
4496
|
+
* renders a native toast / Notification) is not idempotent — every event
|
|
4497
|
+
* stacks. We dedupe at the source: at most one Available per instance
|
|
4498
|
+
* lifetime, regardless of how the upstream `info.version` shifts. The user
|
|
4499
|
+
* already knows "an update exists"; tracking minor version bumps mid-
|
|
4500
|
+
* session is not worth duplicating the toast.
|
|
4501
|
+
*/
|
|
4502
|
+
hasNotified = false;
|
|
4090
4503
|
timer = null;
|
|
4091
4504
|
initialTimer = null;
|
|
4092
4505
|
ipc;
|
|
@@ -4160,9 +4573,10 @@ var UpdateManager = class {
|
|
|
4160
4573
|
}
|
|
4161
4574
|
async checkAndNotify() {
|
|
4162
4575
|
const result = await this.check();
|
|
4163
|
-
if (result.hasUpdate
|
|
4164
|
-
|
|
4165
|
-
|
|
4576
|
+
if (!result.hasUpdate || !result.info) return;
|
|
4577
|
+
if (this.hasNotified) return;
|
|
4578
|
+
this.hasNotified = true;
|
|
4579
|
+
this.mainWindow.webContents.send(UpdateChannel.Available, result.info);
|
|
4166
4580
|
}
|
|
4167
4581
|
};
|
|
4168
4582
|
|
|
@@ -4237,7 +4651,7 @@ async function disposeContext(ctx) {
|
|
|
4237
4651
|
function createConfiguredMainWindow(config, rendererDir2) {
|
|
4238
4652
|
const mainWindow = createMainWindow({
|
|
4239
4653
|
title: config.appName ?? "Dimina DevTools",
|
|
4240
|
-
indexHtml:
|
|
4654
|
+
indexHtml: path16.join(rendererDir2, "entries/main/index.html"),
|
|
4241
4655
|
width: config.window?.width,
|
|
4242
4656
|
height: config.window?.height,
|
|
4243
4657
|
minWidth: config.window?.minWidth,
|
|
@@ -4335,7 +4749,7 @@ function enableDevRendererAutoReload(rendererDir2) {
|
|
|
4335
4749
|
});
|
|
4336
4750
|
}
|
|
4337
4751
|
let reloadTimer = null;
|
|
4338
|
-
const watcher =
|
|
4752
|
+
const watcher = fs10.watch(rendererDir2, { recursive: true }, () => {
|
|
4339
4753
|
if (reloadTimer) clearTimeout(reloadTimer);
|
|
4340
4754
|
reloadTimer = setTimeout(() => {
|
|
4341
4755
|
for (const win of BrowserWindow4.getAllWindows()) {
|
|
@@ -4354,6 +4768,10 @@ function enableDevRendererAutoReload(rendererDir2) {
|
|
|
4354
4768
|
function createWorkbenchApp(config = {}) {
|
|
4355
4769
|
let setupPromise = null;
|
|
4356
4770
|
let appEventsRegistered = false;
|
|
4771
|
+
try {
|
|
4772
|
+
app13.setName(config.appName ?? "Dimina DevTools");
|
|
4773
|
+
} catch {
|
|
4774
|
+
}
|
|
4357
4775
|
setupCdpPort();
|
|
4358
4776
|
registerDifileScheme();
|
|
4359
4777
|
async function setup() {
|
|
@@ -4364,6 +4782,7 @@ function createWorkbenchApp(config = {}) {
|
|
|
4364
4782
|
const mainWindow = createConfiguredMainWindow(config, rendererDir2);
|
|
4365
4783
|
const context = createContext(config, mainWindow, rendererDir2);
|
|
4366
4784
|
context.registry.add(registerAppIpc(context));
|
|
4785
|
+
context.registry.add(registerProjectFsIpc(context));
|
|
4367
4786
|
context.registry.add(installThemeBackgroundSync());
|
|
4368
4787
|
registerBuiltinModules(config, context);
|
|
4369
4788
|
const simSession = session2.fromPartition("persist:simulator");
|
|
@@ -4388,6 +4807,9 @@ function createWorkbenchApp(config = {}) {
|
|
|
4388
4807
|
},
|
|
4389
4808
|
dispose: () => disposeContext(context)
|
|
4390
4809
|
};
|
|
4810
|
+
if (config.onBeforeOpenProject) {
|
|
4811
|
+
context.beforeOpenProject = (projectPath) => config.onBeforeOpenProject({ projectPath, instance });
|
|
4812
|
+
}
|
|
4391
4813
|
if (config.onSetup) {
|
|
4392
4814
|
await config.onSetup(instance);
|
|
4393
4815
|
}
|
|
@@ -4440,7 +4862,7 @@ function createWorkbenchApp(config = {}) {
|
|
|
4440
4862
|
|
|
4441
4863
|
// src/main/windows/settings-window/create.ts
|
|
4442
4864
|
import { BrowserWindow as BrowserWindow5 } from "electron";
|
|
4443
|
-
import
|
|
4865
|
+
import path17 from "path";
|
|
4444
4866
|
async function createSettingsWindow(parent, rendererDir2) {
|
|
4445
4867
|
const win = new BrowserWindow5({
|
|
4446
4868
|
width: 420,
|
|
@@ -4458,7 +4880,7 @@ async function createSettingsWindow(parent, rendererDir2) {
|
|
|
4458
4880
|
}
|
|
4459
4881
|
});
|
|
4460
4882
|
applyNavigationHardening(win.webContents, rendererDir2);
|
|
4461
|
-
await win.loadFile(
|
|
4883
|
+
await win.loadFile(path17.join(rendererDir2, "entries/workbench-settings/index.html"));
|
|
4462
4884
|
return win;
|
|
4463
4885
|
}
|
|
4464
4886
|
|