@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.
Files changed (160) hide show
  1. package/README.md +20 -7
  2. package/dist/main/api.d.ts +1 -1
  3. package/dist/main/app/app.js +17 -0
  4. package/dist/main/index.bundle.js +1006 -584
  5. package/dist/main/ipc/project-fs.d.ts +57 -0
  6. package/dist/main/ipc/project-fs.js +448 -0
  7. package/dist/main/ipc/simulator-module.js +2 -0
  8. package/dist/main/ipc/views.d.ts +19 -0
  9. package/dist/main/ipc/views.js +27 -0
  10. package/dist/main/services/layout/index.d.ts +1 -1
  11. package/dist/main/services/layout/index.js +3 -3
  12. package/dist/main/services/simulator-temp-files/fs-channels.js +1 -1
  13. package/dist/main/services/simulator-temp-files/request-handler.js +1 -1
  14. package/dist/main/services/toolbar/toolbar-store.d.ts +3 -3
  15. package/dist/main/services/toolbar/toolbar-store.js +18 -1
  16. package/dist/main/services/update/update-manager.d.ts +10 -0
  17. package/dist/main/services/update/update-manager.js +16 -3
  18. package/dist/main/services/views/view-manager.d.ts +17 -4
  19. package/dist/main/services/views/view-manager.js +71 -14
  20. package/dist/main/services/workbench-context.d.ts +6 -1
  21. package/dist/main/services/workspace/workspace-service.d.ts +8 -0
  22. package/dist/main/services/workspace/workspace-service.js +22 -4
  23. package/dist/main/utils/ipc-registry.d.ts +10 -1
  24. package/dist/main/utils/ipc-registry.js +43 -0
  25. package/dist/preload/shared/expose.js +2 -2
  26. package/dist/preload/windows/main.cjs +4 -0
  27. package/dist/preload/windows/main.cjs.map +2 -2
  28. package/dist/preload/windows/simulator.js +2 -2
  29. package/dist/renderer/assets/abap-DiwvWnMr.js +2 -0
  30. package/dist/renderer/assets/apex-CmtZjKlf.js +2 -0
  31. package/dist/renderer/assets/azcli-DL2My_i-.js +2 -0
  32. package/dist/renderer/assets/bat-B-nC98wG.js +2 -0
  33. package/dist/renderer/assets/bicep-Ju5MwOgh.js +3 -0
  34. package/dist/renderer/assets/cameligo-8Eu1TyBr.js +2 -0
  35. package/dist/renderer/assets/chunk-DPI0Y_ll.js +1 -0
  36. package/dist/renderer/assets/clojure-u-RpMkH3.js +2 -0
  37. package/dist/renderer/assets/codicon-ngg6Pgfi.ttf +0 -0
  38. package/dist/renderer/assets/coffee-CdA7bbTe.js +2 -0
  39. package/dist/renderer/assets/constants-CLAQX6VZ.js +2 -0
  40. package/dist/renderer/assets/cpp-CzNFP8ks.js +2 -0
  41. package/dist/renderer/assets/csharp-j1LThmcE.js +2 -0
  42. package/dist/renderer/assets/csp-CLRC61y6.js +2 -0
  43. package/dist/renderer/assets/css-r6rC_7P2.js +4 -0
  44. package/dist/renderer/assets/css.worker-DyDu5ynT.js +90 -0
  45. package/dist/renderer/assets/cssMode-BCBgxrnA.js +2 -0
  46. package/dist/renderer/assets/cypher-CW08XVUh.js +2 -0
  47. package/dist/renderer/assets/dart-Cs9aL5T_.js +2 -0
  48. package/dist/renderer/assets/dockerfile-BWM0M184.js +2 -0
  49. package/dist/renderer/assets/ecl-MJJuer5P.js +2 -0
  50. package/dist/renderer/assets/editor-CWI9TnMM.css +1 -0
  51. package/dist/renderer/assets/editor.api2-BTaYmRq7.js +873 -0
  52. package/dist/renderer/assets/editor.worker-Dpt_xPrs.js +27 -0
  53. package/dist/renderer/assets/elixir-D2AIuXqn.js +2 -0
  54. package/dist/renderer/assets/flow9-B2H24giC.js +2 -0
  55. package/dist/renderer/assets/freemarker2-DGDGzimW.js +4 -0
  56. package/dist/renderer/assets/fsharp-CMk2OIJN.js +2 -0
  57. package/dist/renderer/assets/go-BrMkuJg0.js +2 -0
  58. package/dist/renderer/assets/graphql-PSR1UKGv.js +2 -0
  59. package/dist/renderer/assets/handlebars-bOHhw3EG.js +2 -0
  60. package/dist/renderer/assets/hcl-DAQrbDOW.js +2 -0
  61. package/dist/renderer/assets/html-BMyuX0_c.js +2 -0
  62. package/dist/renderer/assets/html.worker-IWNXhnIC.js +503 -0
  63. package/dist/renderer/assets/htmlMode-DR4kXJ7l.js +2 -0
  64. package/dist/renderer/assets/index-BDMxXUfn.js +50 -0
  65. package/dist/renderer/assets/ini-0TG5BxW0.js +2 -0
  66. package/dist/renderer/assets/input-CCYS8Ave.js +2 -0
  67. package/dist/renderer/assets/{ipc-transport-BzPzxPwf.js → ipc-transport-Bn4KXvhu.js} +5 -5
  68. package/dist/renderer/assets/ipc-transport-Cu7tubyg.css +1 -0
  69. package/dist/renderer/assets/java-rgorz17v.js +2 -0
  70. package/dist/renderer/assets/javascript-Bu-tl1P7.js +2 -0
  71. package/dist/renderer/assets/json.worker-c4h5s80L.js +59 -0
  72. package/dist/renderer/assets/jsonMode-MQV_ffLR.js +8 -0
  73. package/dist/renderer/assets/julia-C8VMdHm8.js +2 -0
  74. package/dist/renderer/assets/kotlin-AhhC4Qhm.js +2 -0
  75. package/dist/renderer/assets/less-Cgca25AP.js +3 -0
  76. package/dist/renderer/assets/lexon-D0GHdBaw.js +2 -0
  77. package/dist/renderer/assets/liquid-ClkfOvhB.js +2 -0
  78. package/dist/renderer/assets/lspLanguageFeatures-Bke_4Ie9.js +5 -0
  79. package/dist/renderer/assets/lua-DmRsNG-P.js +2 -0
  80. package/dist/renderer/assets/m3-BgL5dNKT.js +2 -0
  81. package/dist/renderer/assets/markdown-BuJfycGS.js +2 -0
  82. package/dist/renderer/assets/mdx-RZdUskPu.js +2 -0
  83. package/dist/renderer/assets/mips-C9m_93PR.js +2 -0
  84. package/dist/renderer/assets/monaco.contribution-B7rUMaEv.js +3 -0
  85. package/dist/renderer/assets/msdax-CpFHC9OI.js +2 -0
  86. package/dist/renderer/assets/mysql-qFvltsqN.js +2 -0
  87. package/dist/renderer/assets/objective-c-Bnmr858J.js +2 -0
  88. package/dist/renderer/assets/pascal-WP0_D5AO.js +2 -0
  89. package/dist/renderer/assets/pascaligo-Blom4Rij.js +2 -0
  90. package/dist/renderer/assets/perl-B-vk8g64.js +2 -0
  91. package/dist/renderer/assets/pgsql-Cgvz6v67.js +2 -0
  92. package/dist/renderer/assets/php-8a3Lrw9m.js +2 -0
  93. package/dist/renderer/assets/pla-DuFqEZ8V.js +2 -0
  94. package/dist/renderer/assets/popover-CH4rwpdt.js +2 -0
  95. package/dist/renderer/assets/postiats-DkLtSgkp.js +2 -0
  96. package/dist/renderer/assets/powerquery-BJ1aNepW.js +2 -0
  97. package/dist/renderer/assets/powershell-rE98k687.js +2 -0
  98. package/dist/renderer/assets/protobuf-CUheFacr.js +3 -0
  99. package/dist/renderer/assets/pug-LDcAMD8w.js +2 -0
  100. package/dist/renderer/assets/python-Cobrm_-8.js +2 -0
  101. package/dist/renderer/assets/qsharp-DUKSQoR1.js +2 -0
  102. package/dist/renderer/assets/r-D-QApv87.js +2 -0
  103. package/dist/renderer/assets/razor-BEUIQFNJ.js +2 -0
  104. package/dist/renderer/assets/redis-SXdDyWR9.js +2 -0
  105. package/dist/renderer/assets/redshift-Y6lsCryn.js +2 -0
  106. package/dist/renderer/assets/restructuredtext-edObr9a8.js +2 -0
  107. package/dist/renderer/assets/ruby-CNnUfF-8.js +2 -0
  108. package/dist/renderer/assets/rust-IHUZWzBr.js +2 -0
  109. package/dist/renderer/assets/sb-DrUvY44N.js +2 -0
  110. package/dist/renderer/assets/scala-B4hbXGLM.js +2 -0
  111. package/dist/renderer/assets/scheme-BGrd12j3.js +2 -0
  112. package/dist/renderer/assets/scss-x5G1ES4U.js +4 -0
  113. package/dist/renderer/assets/select-Ct4-sijm.js +2 -0
  114. package/dist/renderer/assets/settings-CMTB-t0x.js +2 -0
  115. package/dist/renderer/assets/{settings-api-BXCw9vOZ.js → settings-api-Csdngf9Z.js} +2 -2
  116. package/dist/renderer/assets/shell-DOehe2Y8.js +2 -0
  117. package/dist/renderer/assets/solidity-BeRvcwWV.js +2 -0
  118. package/dist/renderer/assets/sophia-DZbkUNjy.js +2 -0
  119. package/dist/renderer/assets/sparql-B7_oi5-h.js +2 -0
  120. package/dist/renderer/assets/sql-CTlsFWVE.js +2 -0
  121. package/dist/renderer/assets/st-DJVEJdPE.js +2 -0
  122. package/dist/renderer/assets/swift-CwhT3fYa.js +2 -0
  123. package/dist/renderer/assets/systemverilog-BQN63pkN.js +2 -0
  124. package/dist/renderer/assets/tcl-DqwfpskA.js +2 -0
  125. package/dist/renderer/assets/ts.worker-BwM5Ha3L.js +67720 -0
  126. package/dist/renderer/assets/tsMode-BHa7dl9v.js +12 -0
  127. package/dist/renderer/assets/twig-BiyenUgc.js +2 -0
  128. package/dist/renderer/assets/typescript-15aT0Yj8.js +2 -0
  129. package/dist/renderer/assets/typespec-CWOJribt.js +2 -0
  130. package/dist/renderer/assets/vb-Cq5F87m3.js +2 -0
  131. package/dist/renderer/assets/wgsl-BAvW2lVr.js +299 -0
  132. package/dist/renderer/assets/workbenchSettings-CODFCzOg.js +8 -0
  133. package/dist/renderer/assets/workers-CRBS0W-v.js +2 -0
  134. package/dist/renderer/assets/xml-bR2Zh-HV.js +2 -0
  135. package/dist/renderer/assets/yaml-Bdk54Qrb.js +2 -0
  136. package/dist/renderer/entries/main/index.html +29 -6
  137. package/dist/renderer/entries/popover/index.html +7 -6
  138. package/dist/renderer/entries/settings/index.html +6 -5
  139. package/dist/renderer/entries/workbench-settings/index.html +6 -5
  140. package/dist/shared/constants.d.ts +0 -4
  141. package/dist/shared/constants.js +0 -4
  142. package/dist/shared/ipc-channels.d.ts +24 -0
  143. package/dist/shared/ipc-channels.js +37 -0
  144. package/dist/shared/ipc-schemas.d.ts +6 -0
  145. package/dist/shared/ipc-schemas.js +15 -0
  146. package/dist/shared/types.d.ts +37 -0
  147. package/dist/shared/vpath.d.ts +42 -0
  148. package/dist/shared/vpath.js +105 -0
  149. package/dist/simulator/assets/simulator-B4e3c6_O.js +9 -0
  150. package/dist/simulator/simulator.html +1 -1
  151. package/package.json +3 -2
  152. package/dist/renderer/assets/constants-DI382mTP.js +0 -2
  153. package/dist/renderer/assets/index-4lkyF644.js +0 -46
  154. package/dist/renderer/assets/input-D4byFOLS.js +0 -2
  155. package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +0 -1
  156. package/dist/renderer/assets/popover-cxKrg_os.js +0 -2
  157. package/dist/renderer/assets/select-BgSkkdHW.js +0 -2
  158. package/dist/renderer/assets/settings-oD2K1jdT.js +0 -2
  159. package/dist/renderer/assets/workbenchSettings-CTNBpTft.js +0 -8
  160. 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 fs9 from "fs";
109
- import path14 from "path";
739
+ import fs10 from "fs";
740
+ import path16 from "path";
110
741
 
111
742
  // src/main/utils/paths.ts
112
- import fs2 from "fs";
113
- import path2 from "path";
743
+ import fs3 from "fs";
744
+ import path3 from "path";
114
745
  import { fileURLToPath } from "url";
115
746
  function resolveDevtoolsPackageRoot() {
116
- let dir = path2.dirname(fileURLToPath(import.meta.url));
117
- const root = path2.parse(dir).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 = path2.join(dir, "package.json");
750
+ const pkgPath = path3.join(dir, "package.json");
120
751
  try {
121
- const pkg2 = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
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 = path2.dirname(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 = path2.join(devtoolsPackageRoot, "dist/renderer");
131
- var defaultPreloadPath = path2.join(devtoolsPackageRoot, "dist/preload/windows/simulator.js");
132
- var mainPreloadPath = path2.join(devtoolsPackageRoot, "dist/preload/windows/main.cjs");
133
- var simulatorDir = path2.join(devtoolsPackageRoot, "dist/simulator");
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 path4 from "path";
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 path3 from "path";
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 + path3.sep).href;
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 = path4.dirname(opts.indexHtml);
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 path5 from "path";
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: path5.join(app5.getPath("userData"), "dimina-fe-output"),
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 path6 from "path";
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(computeSimulatorBounds(w, h, simWidth, headerHeight));
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
- const tabs = document.querySelectorAll('[role="tab"], .tabbed-pane-header-tab')
719
- for (const tab of tabs) {
720
- const label = (tab.getAttribute('aria-label') || tab.textContent || '').trim().toLowerCase()
721
- if (label === 'console' || label.startsWith('console')) {
722
- tab.click()
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 > 40) clearInterval(timer)
728
- }, 100)
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
- ctx.windows.mainWindow.contentView.addChildView(simulatorView);
735
- simulatorViewAdded = true;
736
- applySimulatorBounds(simWidth);
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
- path6.join(ctx.rendererDir, "entries/settings/index.html")
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
- path6.join(ctx.rendererDir, "entries/popover/index.html")
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 path7 from "path";
921
- import fs3 from "fs";
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 path7.join(app6.getPath("userData"), "dimina-projects.json");
1427
+ return path8.join(app6.getPath("userData"), "dimina-projects.json");
965
1428
  }
966
1429
  function load() {
967
1430
  try {
968
- return JSON.parse(fs3.readFileSync(getProjectsFile(), "utf-8"));
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
- fs3.writeFileSync(getProjectsFile(), JSON.stringify(projects, null, 2));
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 (!fs3.existsSync(dirPath)) {
1446
+ if (!fs4.existsSync(dirPath)) {
984
1447
  return `\u5C0F\u7A0B\u5E8F\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${dirPath}`;
985
1448
  }
986
- if (!fs3.existsSync(path7.join(dirPath, "app.json"))) {
987
- const configPath = path7.join(dirPath, "project.config.json");
988
- if (fs3.existsSync(configPath)) {
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(fs3.readFileSync(configPath, "utf-8"));
1453
+ const cfg = JSON.parse(fs4.readFileSync(configPath, "utf-8"));
991
1454
  if (cfg.miniprogramRoot) {
992
- const resolvedRoot = path7.resolve(dirPath, cfg.miniprogramRoot);
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
- log.warn("Failed to parse project.config.json", err);
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 = path7.basename(dirPath);
1468
+ let name = path8.basename(dirPath);
1006
1469
  try {
1007
- const configPath = path7.join(dirPath, "project.config.json");
1008
- if (fs3.existsSync(configPath)) {
1009
- const cfg = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
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
- log.warn("Failed to read project name from config", err);
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 = path7.join(dirPath, "app.json");
1509
+ const appJsonPath = path8.join(dirPath, "app.json");
1047
1510
  try {
1048
1511
  const appJson = JSON.parse(
1049
- fs3.readFileSync(appJsonPath, "utf-8")
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 = path7.join(projectPath, "project.config.json");
1073
- const config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
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 = path7.join(projectPath, "project.config.json");
1546
+ const configPath = path8.join(projectPath, "project.config.json");
1084
1547
  let config = {};
1085
1548
  try {
1086
- config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
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
- fs3.writeFileSync(
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 fs4 from "fs";
1242
- import path8 from "path";
1714
+ import fs5 from "fs";
1715
+ import path9 from "path";
1243
1716
  import { app as app7 } from "electron";
1244
1717
  function getThumbnailDir() {
1245
- return path8.join(app7.getPath("userData"), "thumbnails");
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 path8.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
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
- fs4.mkdirSync(getThumbnailDir(), { recursive: true });
1258
- fs4.writeFileSync(getThumbnailPath(projectPath), png);
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 = fs4.readFileSync(filePath);
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(({ id, label }) => ({ id, label }));
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 path9 from "node:path";
1360
- var TEMPLATES_DIR = path9.join(devtoolsPackageRoot, "templates");
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: path9.join(TEMPLATES_DIR, "blank") }
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: path9.join(TEMPLATES_DIR, "taro-todo") }
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 fs5 from "fs";
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://${fs5.realpathSync(ctx.preloadPath)}`;
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 = z.string().min(1).refine(
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 = z.tuple([z.string().min(1).max(5e4)]);
1615
- var ProjectsAddSchema = z.tuple([AbsolutePath]);
1616
- var ProjectOpenSchema = z.tuple([AbsolutePath]);
1617
- var CompileConfigShape = z.looseObject({
1618
- startPage: z.string().optional(),
1619
- scene: z.number().optional(),
1620
- queryParams: z.array(z.looseObject({ key: z.string(), value: z.string() })).optional()
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 = z.tuple([
2004
+ var ProjectSaveCompileConfigSchema = z2.tuple([
1623
2005
  AbsolutePath,
1624
2006
  CompileConfigShape
1625
2007
  ]);
1626
- var PopoverShowSchema = z.tuple([z.looseObject({})]);
1627
- var SimWidth = z.number().int().min(100).max(2e3);
1628
- var SimulatorAttachSchema = z.tuple([
1629
- z.number().int().positive(),
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 = z.tuple([SimWidth]);
1633
- var SimulatorSetVisibleSchema = z.tuple([z.boolean(), SimWidth]);
1634
- var SimulatorCustomApiInvokeSchema = z.tuple([
1635
- z.string().min(1).max(256),
1636
- z.unknown()
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 = z.tuple([z.string().min(1).max(256)]);
1639
- var ProjectGetPagesSchema = z.tuple([AbsolutePath]);
1640
- var ProjectGetCompileConfigSchema = z.tuple([AbsolutePath]);
1641
- var ProjectsRemoveSchema = z.tuple([AbsolutePath]);
1642
- var WorkbenchSettingsSaveSchema = z.tuple([
1643
- z.looseObject({
1644
- cdp: z.looseObject({
1645
- enabled: z.boolean(),
1646
- port: z.number().int().min(0).max(65535)
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: z.looseObject({
1649
- enabled: z.boolean(),
1650
- port: z.number().int().min(0).max(65535)
2039
+ mcp: z2.looseObject({
2040
+ enabled: z2.boolean(),
2041
+ port: z2.number().int().min(0).max(65535)
1651
2042
  }),
1652
- compile: z.looseObject({
1653
- watch: z.boolean()
2043
+ compile: z2.looseObject({
2044
+ watch: z2.boolean()
1654
2045
  }),
1655
- theme: z.enum(["system", "light", "dark"]),
1656
- lastCreateBaseDir: z.union([z.string(), z.null()])
2046
+ theme: z2.enum(["system", "light", "dark"]),
2047
+ lastCreateBaseDir: z2.union([z2.string(), z2.null()])
1657
2048
  })
1658
2049
  ]);
1659
- var WorkbenchSettingsSetThemeSchema = z.tuple([
1660
- z.enum(["system", "light", "dark"])
2050
+ var WorkbenchSettingsSetThemeSchema = z2.tuple([
2051
+ z2.enum(["system", "light", "dark"])
1661
2052
  ]);
1662
- var WorkbenchSettingsSetVisibleSchema = z.tuple([z.boolean()]);
1663
- var SettingsSetVisibleSchema = z.tuple([z.boolean()]);
1664
- var SettingsConfigChangedSchema = z.tuple([CompileConfigShape]);
1665
- var PopoverRelaunchSchema = z.tuple([CompileConfigShape]);
1666
- var SettingsProjectSettingsChangedSchema = z.tuple([
1667
- z.looseObject({
1668
- uploadWithSourceMap: z.boolean().optional()
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 = z.tuple([z.string().min(1).max(200)]);
1672
- var ProjectCaptureThumbnailSchema = z.tuple([AbsolutePath]);
1673
- var ProjectGetThumbnailSchema = z.tuple([AbsolutePath]);
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 path11 from "path";
2137
+ import path12 from "path";
1747
2138
 
1748
2139
  // src/main/services/projects/create-project-service.ts
1749
- import fs6 from "node:fs";
1750
- import path10 from "node:path";
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 (fs6.existsSync(target)) {
1762
- const stat = fs6.statSync(target);
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 = fs6.readdirSync(target);
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
- fs6.mkdirSync(target, { recursive: true });
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 (!fs6.existsSync(template.source.path)) {
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
- fs6.cpSync(template.source.path, target, {
2183
+ fs7.cpSync(template.source.path, target, {
1793
2184
  recursive: true,
1794
2185
  force: true
1795
2186
  });
1796
2187
  }
1797
- const cfgPath = path10.join(target, "project.config.json");
2188
+ const cfgPath = path11.join(target, "project.config.json");
1798
2189
  let cfg = {};
1799
- if (fs6.existsSync(cfgPath)) {
2190
+ if (fs7.existsSync(cfgPath)) {
1800
2191
  try {
1801
- cfg = JSON.parse(fs6.readFileSync(cfgPath, "utf-8"));
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
- fs6.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
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 = path11.dirname(input.path);
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 z2 } from "zod";
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: z2.number().optional().default(50).describe("Maximum number of log entries"),
2799
- level: z2.enum(["error", "warning", "warn", "info", "log", "debug"]).optional().describe("Only return entries with this level"),
2800
- sinceTimestamp: z2.string().optional().describe("ISO timestamp; only return entries at or after this time")
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: z2.string().describe("JavaScript expression to evaluate")
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: z2.number().optional().default(3).describe("Maximum 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: z2.number().optional().default(20).describe("Maximum number of entries"),
2836
- minStatus: z2.number().optional().describe("Only return entries with status >= minStatus (status 0 = failed before response is included when minStatus >= 400)")
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 z3 } from "zod";
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: z3.string().optional().describe("URL to navigate to"),
3083
- reload: z3.boolean().optional().describe("If true, reload current page instead of navigating")
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: z3.enum(["tap_coord", "tap_selector", "type", "scroll", "key"]).describe("Input action to dispatch"),
3099
- x: z3.number().optional().describe("Viewport x coordinate for `tap_coord` or `scroll`"),
3100
- y: z3.number().optional().describe("Viewport y coordinate for `tap_coord` or `scroll`"),
3101
- selector: z3.string().optional().describe("CSS selector for `tap_selector` or `type`"),
3102
- nth: z3.number().int().optional().describe("Zero-based index when selector matches multiple elements; defaults to 0"),
3103
- text: z3.string().optional().describe("Text to insert for `type`"),
3104
- key: z3.string().optional().describe("Best-effort keyboard key name for `key`; common keys such as Enter, Escape, Tab, Arrow* and Space work best"),
3105
- deltaX: z3.number().optional().describe("Horizontal wheel delta for `scroll`"),
3106
- deltaY: z3.number().optional().describe("Vertical wheel delta for `scroll`")
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, path16, mime, bytes) {
3965
+ function registerTempFile(store, path18, mime, bytes) {
3562
3966
  const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(new Uint8Array(bytes));
3563
- store.delete(path16);
3564
- store.set(path16, { bytes: buf, mime });
3967
+ store.delete(path18);
3968
+ store.set(path18, { bytes: buf, mime });
3565
3969
  }
3566
- function revokeTempFile(store, path16) {
3567
- store.delete(path16);
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/simulator/vpath.ts
3574
- var _os = typeof __require !== "undefined" ? __require("os") : null;
3575
- var _path = typeof __require !== "undefined" ? __require("path") : null;
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 _path.join(env, "files");
3984
+ return path13.join(env, "files");
3581
3985
  }
3582
- const home = _os ? _os.homedir() : "/tmp";
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 = _path.join(base, decoded);
3618
- const normalized = _path.normalize(joined);
3619
- if (normalized !== base && !normalized.startsWith(base + _path.sep)) return null;
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 fs7 from "node:fs/promises";
3625
- import path12 from "node:path";
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 = path12.extname(realPath).toLowerCase();
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 fs7.open(realPath, "r");
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 fs7.mkdir(path12.dirname(realPath), { recursive: true });
3761
- await fs7.writeFile(realPath, buf);
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 fs7.readdir(realPath);
4167
+ const entries = await fs8.readdir(realPath);
3765
4168
  return entries;
3766
4169
  }
3767
4170
  async function statDiskFile(realPath) {
3768
- const st = await fs7.stat(realPath);
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 fs8 from "node:fs/promises";
3902
- import path13 from "node:path";
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 fs8.realpath(base).catch(() => base);
3909
- const normalized = path13.normalize(realPath);
3910
- if (normalized !== base && !normalized.startsWith(base + path13.sep)) {
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 !== path13.parse(probe).root) {
4317
+ while (probe !== path15.parse(probe).root) {
3915
4318
  try {
3916
- const resolved = await fs8.realpath(probe);
3917
- if (resolved !== baseReal && !resolved.startsWith(baseReal + path13.sep)) {
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 = path13.dirname(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 fs8.unlink(safe);
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 fs8.mkdir(safe, { recursive: !!req.recursive });
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(path16) {
3980
- const list = pendingWaiters.get(path16);
4382
+ function drainWaiters(path18) {
4383
+ const list = pendingWaiters.get(path18);
3981
4384
  if (!list) return;
3982
- pendingWaiters.delete(path16);
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: path16, mime, bytes } = payload;
3995
- registerTempFile(store, path16, mime, bytes);
4397
+ const { path: path18, mime, bytes } = payload;
4398
+ registerTempFile(store, path18, mime, bytes);
3996
4399
  enforceStoreCap(store);
3997
- drainWaiters(path16);
4400
+ drainWaiters(path18);
3998
4401
  });
3999
4402
  registry.on("simulator:temp-file:revoke", (_event, payload) => {
4000
4403
  if (disposed) return;
4001
- const { path: path16 } = payload;
4002
- revokeTempFile(store, path16);
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 && result.info) {
4164
- this.mainWindow.webContents.send(UpdateChannel.Available, result.info);
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: path14.join(rendererDir2, "entries/main/index.html"),
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 = fs9.watch(rendererDir2, { recursive: true }, () => {
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 path15 from "path";
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(path15.join(rendererDir2, "entries/workbench-settings/index.html"));
4883
+ await win.loadFile(path17.join(rendererDir2, "entries/workbench-settings/index.html"));
4462
4884
  return win;
4463
4885
  }
4464
4886