@dimina-kit/devtools 0.3.2-dev.20260601115343 → 0.3.2-dev.20260610114009

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 (189) hide show
  1. package/README.md +49 -59
  2. package/dist/main/api.d.ts +1 -2
  3. package/dist/main/api.js +5 -1
  4. package/dist/main/app/app.d.ts +15 -5
  5. package/dist/main/app/app.js +249 -122
  6. package/dist/main/app/bootstrap.d.ts +16 -0
  7. package/dist/main/app/bootstrap.js +20 -0
  8. package/dist/main/app/launch.d.ts +8 -4
  9. package/dist/main/app/launch.js +9 -4
  10. package/dist/main/index.bundle.js +6071 -1863
  11. package/dist/main/index.js +9 -1
  12. package/dist/main/ipc/app.d.ts +1 -1
  13. package/dist/main/ipc/bridge-router.d.ts +61 -0
  14. package/dist/main/ipc/bridge-router.js +1333 -0
  15. package/dist/main/ipc/panels.d.ts +1 -1
  16. package/dist/main/ipc/panels.js +6 -2
  17. package/dist/main/ipc/popover.d.ts +1 -1
  18. package/dist/main/ipc/project-fs.d.ts +1 -1
  19. package/dist/main/ipc/projects.d.ts +1 -1
  20. package/dist/main/ipc/session.d.ts +1 -1
  21. package/dist/main/ipc/settings.d.ts +1 -1
  22. package/dist/main/ipc/simulator-module.d.ts +6 -0
  23. package/dist/main/ipc/simulator-module.js +9 -1
  24. package/dist/main/ipc/simulator.d.ts +2 -2
  25. package/dist/main/ipc/simulator.js +29 -5
  26. package/dist/main/ipc/toolbar.d.ts +1 -1
  27. package/dist/main/ipc/views.d.ts +1 -1
  28. package/dist/main/ipc/views.js +40 -2
  29. package/dist/main/runtime/devtools-backend.d.ts +17 -0
  30. package/dist/main/runtime/devtools-backend.js +49 -0
  31. package/dist/main/runtime/miniapp-runtime.d.ts +39 -0
  32. package/dist/main/runtime/miniapp-runtime.js +17 -0
  33. package/dist/main/services/automation/exec.d.ts +27 -2
  34. package/dist/main/services/automation/exec.js +37 -3
  35. package/dist/main/services/automation/handlers/app.js +127 -11
  36. package/dist/main/services/automation/handlers/page.js +36 -19
  37. package/dist/main/services/automation/index.js +40 -13
  38. package/dist/main/services/automation/wait-active-page.d.ts +56 -0
  39. package/dist/main/services/automation/wait-active-page.js +71 -0
  40. package/dist/main/services/console-forward/index.d.ts +36 -0
  41. package/dist/main/services/console-forward/index.js +81 -0
  42. package/dist/main/services/dimina-resource-server.d.ts +7 -0
  43. package/dist/main/services/dimina-resource-server.js +99 -0
  44. package/dist/main/services/elements-forward/index.d.ts +66 -0
  45. package/dist/main/services/elements-forward/index.js +583 -0
  46. package/dist/main/services/layout/index.d.ts +22 -0
  47. package/dist/main/services/layout/index.js +25 -0
  48. package/dist/main/services/mcp/index.d.ts +1 -0
  49. package/dist/main/services/mcp/index.js +3 -0
  50. package/dist/main/services/mcp/server.d.ts +1 -1
  51. package/dist/main/services/mcp/server.js +1 -1
  52. package/dist/main/services/mcp/target-manager.d.ts +35 -0
  53. package/dist/main/services/mcp/target-manager.js +63 -1
  54. package/dist/main/services/mcp/tools/context-tools.js +15 -1
  55. package/dist/main/services/module.d.ts +1 -1
  56. package/dist/main/services/network-forward/index.d.ts +190 -0
  57. package/dist/main/services/network-forward/index.js +765 -0
  58. package/dist/main/services/notifications/renderer-notifier.d.ts +11 -0
  59. package/dist/main/services/notifications/renderer-notifier.js +7 -1
  60. package/dist/main/services/render-inspect/index.d.ts +41 -0
  61. package/dist/main/services/render-inspect/index.js +88 -0
  62. package/dist/main/services/safe-area/index.d.ts +17 -0
  63. package/dist/main/services/safe-area/index.js +59 -0
  64. package/dist/main/services/service-host-pool/pool.d.ts +197 -0
  65. package/dist/main/services/service-host-pool/pool.js +364 -0
  66. package/dist/main/services/simulator/custom-apis.d.ts +34 -0
  67. package/dist/main/services/simulator/custom-apis.js +22 -0
  68. package/dist/main/services/simulator-appdata/index.d.ts +35 -0
  69. package/dist/main/services/simulator-appdata/index.js +71 -0
  70. package/dist/main/services/simulator-current-page/index.d.ts +24 -0
  71. package/dist/main/services/simulator-current-page/index.js +19 -0
  72. package/dist/main/services/simulator-storage/index.d.ts +37 -2
  73. package/dist/main/services/simulator-storage/index.js +209 -32
  74. package/dist/main/services/simulator-storage/service-storage-ops.d.ts +40 -0
  75. package/dist/main/services/simulator-storage/service-storage-ops.js +67 -0
  76. package/dist/main/services/simulator-temp-files/index.d.ts +1 -1
  77. package/dist/main/services/simulator-temp-files/index.js +39 -17
  78. package/dist/main/services/simulator-wxml/index.d.ts +29 -0
  79. package/dist/main/services/simulator-wxml/index.js +28 -0
  80. package/dist/main/services/toolbar/toolbar-store.d.ts +3 -3
  81. package/dist/main/services/toolbar/toolbar-store.js +1 -18
  82. package/dist/main/services/views/devtools-tabs.d.ts +47 -0
  83. package/dist/main/services/views/devtools-tabs.js +95 -0
  84. package/dist/main/services/views/miniapp-partition.d.ts +80 -0
  85. package/dist/main/services/views/miniapp-partition.js +138 -0
  86. package/dist/main/services/views/simulator-session-policy.d.ts +28 -0
  87. package/dist/main/services/views/simulator-session-policy.js +82 -0
  88. package/dist/main/services/views/view-manager.d.ts +125 -0
  89. package/dist/main/services/views/view-manager.js +852 -4
  90. package/dist/main/services/workbench-context.d.ts +67 -7
  91. package/dist/main/services/workbench-context.js +6 -1
  92. package/dist/main/services/workspace/workspace-service.js +7 -13
  93. package/dist/main/utils/ipc-registry.d.ts +1 -1
  94. package/dist/main/utils/ipc-registry.js +21 -4
  95. package/dist/main/utils/paths.d.ts +15 -0
  96. package/dist/main/utils/paths.js +19 -0
  97. package/dist/main/utils/sender-policy.d.ts +9 -3
  98. package/dist/main/utils/sender-policy.js +18 -4
  99. package/dist/main/utils/theme.d.ts +1 -1
  100. package/dist/main/utils/theme.js +1 -1
  101. package/dist/main/windows/main-window/create.d.ts +0 -6
  102. package/dist/main/windows/main-window/create.js +6 -80
  103. package/dist/main/windows/main-window/events.d.ts +1 -1
  104. package/dist/main/windows/main-window/events.js +1 -1
  105. package/dist/main/windows/service-host-window/create.d.ts +67 -0
  106. package/dist/main/windows/service-host-window/create.js +138 -0
  107. package/dist/native-host/common/common.js +368 -0
  108. package/dist/native-host/container/pageFrame.css +1 -0
  109. package/dist/native-host/render/render.js +9334 -0
  110. package/dist/native-host/service/service.js +6 -0
  111. package/dist/preload/index.d.ts +2 -0
  112. package/dist/preload/index.js +6 -0
  113. package/dist/preload/instrumentation/app-data.d.ts +18 -20
  114. package/dist/preload/instrumentation/app-data.js +23 -160
  115. package/dist/preload/instrumentation/wxml-extract.d.ts +1 -1
  116. package/dist/preload/instrumentation/wxml-extract.js +1 -1
  117. package/dist/preload/runtime/bridge.d.ts +3 -8
  118. package/dist/preload/runtime/bridge.js +2 -32
  119. package/dist/preload/runtime/custom-apis.js +20 -17
  120. package/dist/preload/runtime/host-toolbar-advertiser.d.ts +27 -0
  121. package/dist/preload/runtime/host-toolbar-advertiser.js +55 -0
  122. package/dist/preload/runtime/main-api-runner.d.ts +9 -0
  123. package/dist/preload/runtime/main-api-runner.js +85 -0
  124. package/dist/preload/runtime/native-host.d.ts +43 -0
  125. package/dist/preload/runtime/native-host.js +83 -0
  126. package/dist/preload/shared/expose.d.ts +10 -5
  127. package/dist/preload/shared/expose.js +30 -12
  128. package/dist/preload/shared/sid-registry.d.ts +11 -0
  129. package/dist/preload/shared/sid-registry.js +38 -0
  130. package/dist/preload/windows/host-toolbar.cjs +161 -0
  131. package/dist/preload/windows/host-toolbar.cjs.map +7 -0
  132. package/dist/preload/windows/host-toolbar.d.ts +2 -0
  133. package/dist/preload/windows/host-toolbar.js +12 -0
  134. package/dist/preload/windows/main.cjs +1935 -0
  135. package/dist/preload/windows/main.cjs.map +4 -4
  136. package/dist/preload/windows/simulator.cjs +925 -0
  137. package/dist/preload/windows/simulator.cjs.map +7 -0
  138. package/dist/preload/windows/simulator.js +244 -574
  139. package/dist/render-host/pageFrame.html +29 -0
  140. package/dist/render-host/preload.cjs +154 -0
  141. package/dist/render-host/render-inspect.js +295 -0
  142. package/dist/renderer/assets/index-CcJKzatF.js +50 -0
  143. package/dist/renderer/assets/{input-CCYS8Ave.js → input-QGx6FFX1.js} +2 -2
  144. package/dist/renderer/assets/ipc-transport-BT4QYQqu.css +1 -0
  145. package/dist/renderer/assets/{ipc-transport-Bn4KXvhu.js → ipc-transport-DvZvwK-Z.js} +2 -2
  146. package/dist/renderer/assets/{popover-CH4rwpdt.js → popover-BOH1If1J.js} +2 -2
  147. package/dist/renderer/assets/select-C7JQ8G5N.js +2 -0
  148. package/dist/renderer/assets/{settings-CMTB-t0x.js → settings-DpAbTJyF.js} +2 -2
  149. package/dist/renderer/assets/{settings-api-Csdngf9Z.js → settings-api-RD2IZF53.js} +2 -2
  150. package/dist/renderer/assets/{workbenchSettings-CODFCzOg.js → workbenchSettings-CNJX4Qna.js} +2 -2
  151. package/dist/renderer/entries/main/index.html +5 -5
  152. package/dist/renderer/entries/popover/index.html +5 -5
  153. package/dist/renderer/entries/settings/index.html +5 -5
  154. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  155. package/dist/service-host/preload.cjs +254 -0
  156. package/dist/service-host/service.html +10 -0
  157. package/dist/service-host/sourcemap-rewrite.cjs +58 -0
  158. package/dist/service-host/sync-api-patch.js +20 -0
  159. package/dist/service-host/sync-impls/menu-button-geometry.js +8 -0
  160. package/dist/service-host/sync-impls/menu-button.js +16 -0
  161. package/dist/service-host/sync-impls/storage.js +50 -0
  162. package/dist/service-host/sync-impls/system-info.js +54 -0
  163. package/dist/shared/appdata-accumulator.d.ts +95 -0
  164. package/dist/shared/appdata-accumulator.js +213 -0
  165. package/dist/shared/bridge-channels.d.ts +378 -0
  166. package/dist/shared/bridge-channels.js +81 -0
  167. package/dist/shared/ipc-channels.d.ts +84 -1
  168. package/dist/shared/ipc-channels.js +90 -10
  169. package/dist/shared/ipc-schemas.d.ts +59 -2
  170. package/dist/shared/ipc-schemas.js +64 -3
  171. package/dist/shared/open-in-editor.d.ts +71 -0
  172. package/dist/shared/open-in-editor.js +138 -0
  173. package/dist/shared/simulator-api-metadata.d.ts +21 -0
  174. package/dist/shared/simulator-api-metadata.js +23 -0
  175. package/dist/shared/types.d.ts +2 -39
  176. package/dist/simulator/assets/device-shell-DsdcNEkt.css +1 -0
  177. package/dist/simulator/assets/device-shell-fH2cI-3q.js +2 -0
  178. package/dist/simulator/assets/jsx-runtime-P6ZegaOf.js +2 -0
  179. package/dist/simulator/assets/simulator-DHylZf9Z.js +10 -0
  180. package/dist/simulator/assets/simulator-mini-app-BDNu5n26.js +2 -0
  181. package/dist/simulator/device-shell/menu-button-geometry.js +8 -0
  182. package/dist/simulator/simulator.html +2 -112
  183. package/package.json +12 -12
  184. package/dist/main/utils/disposable.d.ts +0 -18
  185. package/dist/main/utils/disposable.js +0 -67
  186. package/dist/renderer/assets/index-BDMxXUfn.js +0 -50
  187. package/dist/renderer/assets/ipc-transport-Cu7tubyg.css +0 -1
  188. package/dist/renderer/assets/select-Ct4-sijm.js +0 -2
  189. package/dist/simulator/assets/simulator-B4e3c6_O.js +0 -9
@@ -16,9 +16,18 @@
16
16
  * events stop flowing until they close it.
17
17
  */
18
18
  import { app, webContents as wcStatic } from 'electron';
19
+ import { DisposableRegistry } from '@dimina-kit/electron-deck/main';
19
20
  import { SimulatorElementChannel, SimulatorStorageChannel, } from '../../../shared/ipc-channels.js';
20
- import { DisposableRegistry } from '../../utils/disposable.js';
21
21
  import { IpcRegistry } from '../../utils/ipc-registry.js';
22
+ import { decodeStorageValue, encodeStorageValue, serviceStorage } from './service-storage-ops.js';
23
+ /** Async wx storage API names routed through the unified service-window store. */
24
+ export const STORAGE_API_NAMES = new Set([
25
+ 'setStorage',
26
+ 'getStorage',
27
+ 'removeStorage',
28
+ 'clearStorage',
29
+ 'getStorageInfo',
30
+ ]);
22
31
  function isSimulatorWebview(wc) {
23
32
  if (wc.isDestroyed())
24
33
  return false;
@@ -39,6 +48,15 @@ function safeOff(target, event, fn) {
39
48
  export function setupSimulatorStorage(host, options) {
40
49
  const registry = new DisposableRegistry();
41
50
  const getActiveAppId = options.getActiveAppId;
51
+ // Native-host: the mini-app's storage lives on the service-host window's
52
+ // file:// origin (sync-impls + the unified async path both write there), not
53
+ // the simulator <webview>'s http:// origin the CDP path attaches to. Resolve
54
+ // the service window fresh each call (the pre-warm pool can swap it on respawn).
55
+ function nativeServiceWc() {
56
+ if (!options.bridge?.isNativeHost())
57
+ return null;
58
+ return options.bridge.getServiceWc(getActiveAppId() ?? undefined);
59
+ }
42
60
  let attachedWc = null;
43
61
  let attachDisposables = null;
44
62
  /**
@@ -107,8 +125,23 @@ export function setupSimulatorStorage(host, options) {
107
125
  void ad.disposeAll().catch(() => { });
108
126
  }
109
127
  };
110
- wc.once('destroyed', onDestroyed);
111
- attach.add(() => safeOff(wc, 'destroyed', onDestroyed));
128
+ if (options.connections) {
129
+ // Route through the connection layer: own() disposes onDestroyed
130
+ // deterministically on wc destroy / connection reset, and the returned
131
+ // Disposable lets us release it early when this attach segment tears
132
+ // down (detach / re-attach).
133
+ const owned = options.connections.acquire(wc).own(onDestroyed);
134
+ attach.add(() => owned.dispose());
135
+ }
136
+ else {
137
+ try {
138
+ wc.once('destroyed', onDestroyed);
139
+ }
140
+ catch {
141
+ // fake-wc safety (unit tests pass a wc without a real emitter)
142
+ }
143
+ attach.add(() => safeOff(wc, 'destroyed', onDestroyed));
144
+ }
112
145
  attachedWc = wc;
113
146
  }
114
147
  catch (e) {
@@ -257,6 +290,17 @@ export function setupSimulatorStorage(host, options) {
257
290
  host.send(SimulatorStorageChannel.Event, evt);
258
291
  }
259
292
  async function setItem(key, value) {
293
+ const nativeWc = nativeServiceWc();
294
+ if (nativeWc) {
295
+ try {
296
+ await serviceStorage(nativeWc).writeOne(key, value);
297
+ pushSyntheticEvent({ type: 'added', key, newValue: value });
298
+ return { ok: true };
299
+ }
300
+ catch (e) {
301
+ return { ok: false, error: e.message };
302
+ }
303
+ }
260
304
  return withStorageWrite(async ({ wc, storageId }) => {
261
305
  await wc.debugger.sendCommand('DOMStorage.setDOMStorageItem', { storageId, key, value });
262
306
  pushSyntheticEvent({ type: 'added', key, newValue: value });
@@ -264,6 +308,17 @@ export function setupSimulatorStorage(host, options) {
264
308
  });
265
309
  }
266
310
  async function removeItem(key) {
311
+ const nativeWc = nativeServiceWc();
312
+ if (nativeWc) {
313
+ try {
314
+ await serviceStorage(nativeWc).removeOne(key);
315
+ pushSyntheticEvent({ type: 'removed', key });
316
+ return { ok: true };
317
+ }
318
+ catch (e) {
319
+ return { ok: false, error: e.message };
320
+ }
321
+ }
267
322
  return withStorageWrite(async ({ wc, storageId }) => {
268
323
  await wc.debugger.sendCommand('DOMStorage.removeDOMStorageItem', { storageId, key });
269
324
  pushSyntheticEvent({ type: 'removed', key });
@@ -275,6 +330,23 @@ export function setupSimulatorStorage(host, options) {
275
330
  // Falls back to origin-wide `DOMStorage.clear` only when no active appId is
276
331
  // set (matches the snapshot filter's documented null-fallback).
277
332
  async function clearScoped() {
333
+ const nativeWc = nativeServiceWc();
334
+ if (nativeWc) {
335
+ try {
336
+ const prefix = activePrefix();
337
+ const ss = serviceStorage(nativeWc);
338
+ if (prefix)
339
+ await ss.clearPrefix(prefix);
340
+ else
341
+ await ss.clearAll();
342
+ if (!host.isDestroyed())
343
+ host.send(SimulatorStorageChannel.Event, { type: 'cleared' });
344
+ return { ok: true };
345
+ }
346
+ catch (e) {
347
+ return { ok: false, error: e.message };
348
+ }
349
+ }
278
350
  return withStorageWrite(async ({ wc, storageId }) => {
279
351
  const prefix = activePrefix();
280
352
  if (!prefix) {
@@ -296,12 +368,33 @@ export function setupSimulatorStorage(host, options) {
296
368
  // state in the shared simulator partition. The renderer guards this with a
297
369
  // confirm dialog; we don't second-guess here.
298
370
  async function clearAll() {
371
+ const nativeWc = nativeServiceWc();
372
+ if (nativeWc) {
373
+ try {
374
+ await serviceStorage(nativeWc).clearAll();
375
+ if (!host.isDestroyed())
376
+ host.send(SimulatorStorageChannel.Event, { type: 'cleared' });
377
+ return { ok: true };
378
+ }
379
+ catch (e) {
380
+ return { ok: false, error: e.message };
381
+ }
382
+ }
299
383
  return withStorageWrite(async ({ wc, storageId }) => {
300
384
  await wc.debugger.sendCommand('DOMStorage.clear', { storageId });
301
385
  return { ok: true };
302
386
  });
303
387
  }
304
388
  async function getSnapshot() {
389
+ const nativeWc = nativeServiceWc();
390
+ if (nativeWc) {
391
+ const prefix = activePrefix();
392
+ if (!prefix)
393
+ return [];
394
+ const entries = await serviceStorage(nativeWc).readAll(prefix);
395
+ // Keys returned with the `${appId}_` prefix intact (same as the CDP path).
396
+ return entries.map(([key, value]) => ({ key, value }));
397
+ }
305
398
  return withStorageRead([], async ({ wc, storageId }) => {
306
399
  const result = (await wc.debugger.sendCommand('DOMStorage.getDOMStorageItems', {
307
400
  storageId,
@@ -337,10 +430,24 @@ export function setupSimulatorStorage(host, options) {
337
430
  };
338
431
  wc.on('did-finish-load', onFinishLoad);
339
432
  sub.add(() => safeOff(wc, 'did-finish-load', onFinishLoad));
340
- wc.once('destroyed', () => {
433
+ const onWcDestroyed = () => {
341
434
  void sub.disposeAll().catch(() => { });
342
435
  wcSubs.delete(wc);
343
- });
436
+ };
437
+ if (options.connections) {
438
+ // Per-wc cleanup (this wc's did-finish-load sub + wcSubs entry). Route
439
+ // through the connection layer so it fires deterministically on wc
440
+ // destroy / reset rather than the bespoke 'destroyed' hook.
441
+ options.connections.acquire(wc).own(onWcDestroyed);
442
+ }
443
+ else {
444
+ try {
445
+ wc.once('destroyed', onWcDestroyed);
446
+ }
447
+ catch {
448
+ // fake-wc safety (unit tests pass a wc without a real emitter)
449
+ }
450
+ }
344
451
  };
345
452
  app.on('web-contents-created', onWcCreated);
346
453
  registry.add(() => {
@@ -353,6 +460,49 @@ export function setupSimulatorStorage(host, options) {
353
460
  await sub.disposeAll().catch(() => { });
354
461
  }
355
462
  });
463
+ // Element inspection. Default path delegates to the simulator iframe's
464
+ // `__simulatorData`. Native-host path targets the active render-host
465
+ // <webview> guest via the render inspector (the page DOM lives there, not in
466
+ // the simulator iframe). Looked up fresh each call so a DevTools-induced CDP
467
+ // detach (default path) or a render-guest swap (native path) is tolerated.
468
+ async function inspectElement(sid) {
469
+ if (!sid)
470
+ return null;
471
+ if (options.bridge?.isNativeHost() && options.renderInspector) {
472
+ const wc = options.bridge.getActiveRenderWc(getActiveAppId() ?? undefined);
473
+ if (!wc)
474
+ return null;
475
+ return options.renderInspector.highlight(wc, sid);
476
+ }
477
+ const sim = findSimulatorWebContents();
478
+ if (!sim)
479
+ return null;
480
+ try {
481
+ const result = (await sim.executeJavaScript(`window.__simulatorData && window.__simulatorData.highlightElement ? window.__simulatorData.highlightElement(${JSON.stringify(sid)}) : null`));
482
+ return result ?? null;
483
+ }
484
+ catch (e) {
485
+ console.warn('[simulator-element] inspect failed:', e.message);
486
+ return null;
487
+ }
488
+ }
489
+ async function clearElementInspection() {
490
+ if (options.bridge?.isNativeHost() && options.renderInspector) {
491
+ const wc = options.bridge.getActiveRenderWc(getActiveAppId() ?? undefined);
492
+ if (wc)
493
+ await options.renderInspector.unhighlight(wc);
494
+ return;
495
+ }
496
+ const sim = findSimulatorWebContents();
497
+ if (!sim)
498
+ return;
499
+ try {
500
+ await sim.executeJavaScript('window.__simulatorData && window.__simulatorData.unhighlightElement && window.__simulatorData.unhighlightElement()');
501
+ }
502
+ catch {
503
+ // best-effort
504
+ }
505
+ }
356
506
  // IPC handlers — gated by the workbench SenderPolicy when provided so this
357
507
  // module routes through the same C-stage sender white-list as every other
358
508
  // workbench-built-in (main-window renderer + overlay views only). Without
@@ -370,7 +520,60 @@ export function setupSimulatorStorage(host, options) {
370
520
  registry.add(ipc);
371
521
  // Detach active CDP session last
372
522
  registry.add(() => detachFromSim());
373
- return registry;
523
+ // ── Runtime async-storage unification (native-host) ───────────────────────
524
+ // bridge-router routes async wx.setStorage/etc. here so they hit the SAME
525
+ // service-host file:// store as the sync APIs (no more split origin). After a
526
+ // write we push a synthetic panel event so the Storage panel stays live on
527
+ // in-app writes (executeJavaScript writes don't emit CDP DOMStorage events).
528
+ async function runtimeInvoke(appId, name, params) {
529
+ const wc = options.bridge?.getServiceWc(appId);
530
+ if (!wc)
531
+ throw new Error('service-host window unavailable');
532
+ const ss = serviceStorage(wc);
533
+ const prefix = `${appId}_`;
534
+ const key = typeof params.key === 'string' ? params.key : '';
535
+ const fullKey = prefix + key;
536
+ switch (name) {
537
+ case 'setStorage': {
538
+ const value = encodeStorageValue(params.data);
539
+ await ss.writeOne(fullKey, value);
540
+ pushSyntheticEvent({ type: 'added', key: fullKey, newValue: value });
541
+ return { errMsg: 'setStorage:ok' };
542
+ }
543
+ case 'getStorage': {
544
+ const raw = await ss.readOne(fullKey);
545
+ if (raw === null)
546
+ throw new Error('getStorage:fail data not found');
547
+ return { data: decodeStorageValue(raw), errMsg: 'getStorage:ok' };
548
+ }
549
+ case 'removeStorage': {
550
+ await ss.removeOne(fullKey);
551
+ pushSyntheticEvent({ type: 'removed', key: fullKey });
552
+ return { errMsg: 'removeStorage:ok' };
553
+ }
554
+ case 'clearStorage': {
555
+ await ss.clearPrefix(prefix);
556
+ if (appId === getActiveAppId() && !host.isDestroyed()) {
557
+ host.send(SimulatorStorageChannel.Event, { type: 'cleared' });
558
+ }
559
+ return { errMsg: 'clearStorage:ok' };
560
+ }
561
+ case 'getStorageInfo': {
562
+ const entries = await ss.readAll(prefix);
563
+ const keys = entries.map(([k]) => k.slice(prefix.length));
564
+ let currentSize = 0;
565
+ for (const [, v] of entries)
566
+ currentSize += v ? v.length * 2 : 0;
567
+ return { keys, currentSize, limitSize: 10 * 1024 * 1024, errMsg: 'getStorageInfo:ok' };
568
+ }
569
+ default:
570
+ throw new Error(`unsupported storage api ${name}`);
571
+ }
572
+ }
573
+ const storageApi = options.bridge?.isNativeHost()
574
+ ? { invoke: runtimeInvoke }
575
+ : null;
576
+ return { dispose: () => registry.dispose(), storageApi };
374
577
  }
375
578
  // Element inspection delegates to the simulator preload's __simulatorData
376
579
  // bridge via executeJavaScript. Looked up independently from `attachedWc` so
@@ -383,30 +586,4 @@ function findSimulatorWebContents() {
383
586
  }
384
587
  return null;
385
588
  }
386
- async function inspectElement(sid) {
387
- if (!sid)
388
- return null;
389
- const sim = findSimulatorWebContents();
390
- if (!sim)
391
- return null;
392
- try {
393
- const result = (await sim.executeJavaScript(`window.__simulatorData && window.__simulatorData.highlightElement ? window.__simulatorData.highlightElement(${JSON.stringify(sid)}) : null`));
394
- return result ?? null;
395
- }
396
- catch (e) {
397
- console.warn('[simulator-element] inspect failed:', e.message);
398
- return null;
399
- }
400
- }
401
- async function clearElementInspection() {
402
- const sim = findSimulatorWebContents();
403
- if (!sim)
404
- return;
405
- try {
406
- await sim.executeJavaScript('window.__simulatorData && window.__simulatorData.unhighlightElement && window.__simulatorData.unhighlightElement()');
407
- }
408
- catch {
409
- // best-effort
410
- }
411
- }
412
589
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * service-storage-ops — localStorage primitives executed INSIDE the service-host
3
+ * window (`wc`) via `executeJavaScript`, plus the wx-storage value encode/decode
4
+ * pair.
5
+ *
6
+ * Under native-host the mini-app's storage lives on the service-host window's
7
+ * `file://` origin (sync APIs write there via sync-impls/storage.ts, and the
8
+ * unified async path routes there too). The Storage panel + the async-storage
9
+ * runtime both read/write through here so there is exactly ONE store.
10
+ *
11
+ * The key/value scheme stays byte-compatible with
12
+ * `src/service-host/sync-impls/storage.ts` (the authoritative wx storage impl):
13
+ * - key = `${appId}_${key}`
14
+ * - value = `typeof data === 'object' ? JSON.stringify(data) : String(data)`
15
+ * - scoped clear removes every key whose name startsWith `${appId}_`
16
+ *
17
+ * Args are embedded via JSON.stringify so special characters can't break out of
18
+ * the injected code. Reads tolerate failure (→ []/null); writes propagate so the
19
+ * caller can fail the wx callback / surface the panel error.
20
+ */
21
+ import type { WebContents } from 'electron';
22
+ /** Encode a wx storage value to its localStorage string form. Matches sync-impls/storage.ts. */
23
+ export declare function encodeStorageValue(data: unknown): string;
24
+ /** Decode a localStorage string back to a value (JSON.parse, falling back to the raw string). */
25
+ export declare function decodeStorageValue(raw: string): unknown;
26
+ export interface ServiceStorage {
27
+ /** All [fullKey, value] entries whose key startsWith `prefix`. */
28
+ readAll(prefix: string): Promise<Array<[string, string]>>;
29
+ /** Raw value for a full (already-prefixed) key, or null if absent. */
30
+ readOne(fullKey: string): Promise<string | null>;
31
+ writeOne(fullKey: string, value: string): Promise<void>;
32
+ removeOne(fullKey: string): Promise<void>;
33
+ /** Remove every key whose name startsWith `prefix`. */
34
+ clearPrefix(prefix: string): Promise<void>;
35
+ /** Origin-wide localStorage.clear(). */
36
+ clearAll(): Promise<void>;
37
+ }
38
+ /** localStorage ops executed inside `wc` (the service-host window) via executeJavaScript. */
39
+ export declare function serviceStorage(wc: WebContents): ServiceStorage;
40
+ //# sourceMappingURL=service-storage-ops.d.ts.map
@@ -0,0 +1,67 @@
1
+ /** Encode a wx storage value to its localStorage string form. Matches sync-impls/storage.ts. */
2
+ export function encodeStorageValue(data) {
3
+ return typeof data === 'object' ? JSON.stringify(data) : String(data);
4
+ }
5
+ /** Decode a localStorage string back to a value (JSON.parse, falling back to the raw string). */
6
+ export function decodeStorageValue(raw) {
7
+ try {
8
+ return JSON.parse(raw);
9
+ }
10
+ catch {
11
+ return raw;
12
+ }
13
+ }
14
+ /** localStorage ops executed inside `wc` (the service-host window) via executeJavaScript. */
15
+ export function serviceStorage(wc) {
16
+ return {
17
+ async readAll(prefix) {
18
+ const code = `(() => {
19
+ const out = []
20
+ const p = ${JSON.stringify(prefix)}
21
+ for (let i = 0; i < localStorage.length; i++) {
22
+ const k = localStorage.key(i)
23
+ if (k && k.startsWith(p)) out.push([k, localStorage.getItem(k)])
24
+ }
25
+ return out
26
+ })()`;
27
+ try {
28
+ const result = await wc.executeJavaScript(code);
29
+ return Array.isArray(result) ? result : [];
30
+ }
31
+ catch {
32
+ return [];
33
+ }
34
+ },
35
+ async readOne(fullKey) {
36
+ try {
37
+ const result = await wc.executeJavaScript(`localStorage.getItem(${JSON.stringify(fullKey)})`);
38
+ return result === null || result === undefined ? null : String(result);
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ },
44
+ async writeOne(fullKey, value) {
45
+ await wc.executeJavaScript(`localStorage.setItem(${JSON.stringify(fullKey)}, ${JSON.stringify(value)})`);
46
+ },
47
+ async removeOne(fullKey) {
48
+ await wc.executeJavaScript(`localStorage.removeItem(${JSON.stringify(fullKey)})`);
49
+ },
50
+ async clearPrefix(prefix) {
51
+ const code = `(() => {
52
+ const p = ${JSON.stringify(prefix)}
53
+ const doomed = []
54
+ for (let i = 0; i < localStorage.length; i++) {
55
+ const k = localStorage.key(i)
56
+ if (k && k.startsWith(p)) doomed.push(k)
57
+ }
58
+ doomed.forEach((k) => localStorage.removeItem(k))
59
+ })()`;
60
+ await wc.executeJavaScript(code);
61
+ },
62
+ async clearAll() {
63
+ await wc.executeJavaScript('localStorage.clear()');
64
+ },
65
+ };
66
+ }
67
+ //# sourceMappingURL=service-storage-ops.js.map
@@ -28,6 +28,6 @@
28
28
  * size cap), simplified for a dev-tools session scope.
29
29
  */
30
30
  import type { Session } from 'electron';
31
- import { type Disposable } from '../../utils/disposable.js';
31
+ import { type Disposable } from '@dimina-kit/electron-deck/main';
32
32
  export declare function setupSimulatorTempFiles(simSession: Session): Disposable;
33
33
  //# sourceMappingURL=index.d.ts.map
@@ -28,9 +28,10 @@
28
28
  * size cap), simplified for a dev-tools session scope.
29
29
  */
30
30
  import { IpcRegistry } from '../../utils/ipc-registry.js';
31
- import { toDisposable } from '../../utils/disposable.js';
31
+ import { toDisposable } from '@dimina-kit/electron-deck/main';
32
32
  import { registerTempFile, revokeTempFile, revokeAllTempFiles } from './store.js';
33
33
  import { handleDifileRequest } from './request-handler.js';
34
+ import { registerMiniappSessionConfigurator } from '../views/miniapp-partition.js';
34
35
  import { handleFsMkdir, handleFsRead, handleFsReaddir, handleFsStat, handleFsUnlink, handleFsWrite, } from './fs-channels.js';
35
36
  /** Upper bound for in-memory entries; oldest insertion is evicted (FIFO). */
36
37
  const MAX_STORE_ENTRIES = 200;
@@ -63,7 +64,12 @@ export function setupSimulatorTempFiles(simSession) {
63
64
  for (const fn of list)
64
65
  fn();
65
66
  }
66
- const simulatorOnlyPolicy = sender => !sender.isDestroyed() && sender.session === simSession;
67
+ // The protocol handler + IPC channels are shared across every per-project
68
+ // miniapp partition session (each project's render/service runs the same
69
+ // difile:// + temp-file FSM). The base session is always trusted; additional
70
+ // per-project partition sessions register themselves via `installOnSession`.
71
+ const trustedSessions = new Set([simSession]);
72
+ const simulatorOnlyPolicy = sender => !sender.isDestroyed() && trustedSessions.has(sender.session);
67
73
  const registry = new IpcRegistry(simulatorOnlyPolicy);
68
74
  registry.on('simulator:temp-file:write', (_event, payload) => {
69
75
  if (disposed)
@@ -84,15 +90,7 @@ export function setupSimulatorTempFiles(simSession) {
84
90
  return;
85
91
  revokeAllTempFiles(store);
86
92
  });
87
- // Idempotent: a stale handler from a prior setup (e.g. fast app
88
- // re-init in tests) is replaced rather than throwing.
89
- try {
90
- simSession.protocol.unhandle('difile');
91
- }
92
- catch {
93
- // Not previously registered — fine.
94
- }
95
- simSession.protocol.handle('difile', async (req) => {
93
+ const difileHandler = async (req) => {
96
94
  const url = req.url;
97
95
  // Forward any HTTP headers Electron parsed (Range / If-None-Match)
98
96
  // to the pure dispatcher so it can do its own conditional / range
@@ -135,7 +133,28 @@ export function setupSimulatorTempFiles(simSession) {
135
133
  res = await handleDifileRequest(ctx, { url, headers });
136
134
  }
137
135
  return res;
138
- });
136
+ };
137
+ // Install the difile:// protocol handler on a session. Idempotent per
138
+ // session: a stale handler from a prior setup (e.g. fast app re-init in
139
+ // tests) is replaced rather than throwing. Trusts that session's senders for
140
+ // the temp-file / fs IPC channels.
141
+ const installedSessions = new Set();
142
+ function installOnSession(sess) {
143
+ trustedSessions.add(sess);
144
+ if (installedSessions.has(sess))
145
+ return;
146
+ installedSessions.add(sess);
147
+ try {
148
+ sess.protocol.unhandle('difile');
149
+ }
150
+ catch {
151
+ // Not previously registered — fine.
152
+ }
153
+ sess.protocol.handle('difile', difileHandler);
154
+ }
155
+ installOnSession(simSession);
156
+ // Apply to every per-project miniapp partition session (current + future).
157
+ const unregisterConfigurator = registerMiniappSessionConfigurator((sess) => installOnSession(sess));
139
158
  // Phase 1 (P1-7): renderer FSM → main fs operations bridge. The same
140
159
  // simulator-only sender policy applies — registry instance is shared.
141
160
  registry.handle('simulator:fs:read', (_event, payload) => handleFsRead(payload));
@@ -146,13 +165,16 @@ export function setupSimulatorTempFiles(simSession) {
146
165
  registry.handle('simulator:fs:mkdir', (_event, payload) => handleFsMkdir(payload));
147
166
  return toDisposable(async () => {
148
167
  disposed = true;
168
+ unregisterConfigurator();
149
169
  drainAllWaiters();
150
170
  store.clear();
151
- try {
152
- simSession.protocol.unhandle('difile');
153
- }
154
- catch {
155
- // May already have been unhandled by app shutdown.
171
+ for (const sess of installedSessions) {
172
+ try {
173
+ sess.protocol.unhandle('difile');
174
+ }
175
+ catch {
176
+ // May already have been unhandled by app shutdown.
177
+ }
156
178
  }
157
179
  await registry.dispose();
158
180
  });
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Native-host WXML panel service.
3
+ *
4
+ * Mirrors simulator-storage's main→renderer contract so the renderer WXML panel
5
+ * needs no per-mode branching of its own data shape:
6
+ * - PULL: answers `SimulatorWxmlChannel.GetSnapshot` by reading the active
7
+ * render-host guest's WXML tree via the injected RenderInspector.
8
+ * - PUSH: on bridge render-side activity (domReady / active-page change),
9
+ * re-reads the tree and pushes `SimulatorWxmlChannel.Event` to the renderer
10
+ * host — keeping the panel reactive without polling.
11
+ *
12
+ * Only meaningful under native-host (the default dimina-fe path sources WXML
13
+ * from the simulator guest's miniappSnapshot transport); app.ts gates the call
14
+ * on `ctx.bridge.isNativeHost()`.
15
+ */
16
+ import type { WebContents } from 'electron';
17
+ import type { BridgeRouterHandle } from '../../ipc/bridge-router.js';
18
+ import { type Disposable } from '@dimina-kit/electron-deck/main';
19
+ import { type SenderPolicy } from '../../utils/ipc-registry.js';
20
+ import type { RenderInspector } from '../render-inspect/index.js';
21
+ export interface SimulatorWxmlOptions {
22
+ bridge: BridgeRouterHandle;
23
+ inspector: RenderInspector;
24
+ getActiveAppId: () => string | null;
25
+ /** Sender gate applied to the WXML IPC handler; omitted in unit tests. */
26
+ senderPolicy?: SenderPolicy;
27
+ }
28
+ export declare function setupSimulatorWxml(host: WebContents, options: SimulatorWxmlOptions): Disposable;
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { SimulatorWxmlChannel } from '../../../shared/ipc-channels.js';
2
+ import { DisposableRegistry } from '@dimina-kit/electron-deck/main';
3
+ import { IpcRegistry } from '../../utils/ipc-registry.js';
4
+ export function setupSimulatorWxml(host, options) {
5
+ const { bridge, inspector, getActiveAppId } = options;
6
+ const registry = new DisposableRegistry();
7
+ /** Read the WXML tree of the visible page of the active app, or null. */
8
+ async function pull() {
9
+ const wc = bridge.getActiveRenderWc(getActiveAppId() ?? undefined);
10
+ if (!wc)
11
+ return null;
12
+ return inspector.getWxml(wc);
13
+ }
14
+ const ipc = new IpcRegistry(options.senderPolicy);
15
+ ipc.handle(SimulatorWxmlChannel.GetSnapshot, () => pull());
16
+ registry.add(ipc);
17
+ // Re-read + push when a page's DOM mounts or the visible page changes.
18
+ const unsubscribe = bridge.onRenderEvent(() => {
19
+ void pull().then((tree) => {
20
+ if (host.isDestroyed())
21
+ return;
22
+ host.send(SimulatorWxmlChannel.Event, tree);
23
+ });
24
+ });
25
+ registry.add(unsubscribe);
26
+ return registry;
27
+ }
28
+ //# sourceMappingURL=index.js.map
@@ -4,8 +4,8 @@ import type { ToolbarAction, ToolbarActionInput } from '../../../shared/types.js
4
4
  *
5
5
  * The toolbar is modelled as "one table the host recomputes from its current
6
6
  * state": `set()` replaces the whole table atomically. The non-serialisable
7
- * `handler` is kept main-process side; `list()` projects display metadata for
8
- * the IPC boundary, and `getHandler()` resolves a handler by id.
7
+ * `handler` is kept main-process side; `list()` projects to `{id,label}[]`
8
+ * for the IPC boundary, and `getHandler()` resolves a handler by id.
9
9
  */
10
10
  export interface ToolbarStore {
11
11
  /**
@@ -14,7 +14,7 @@ export interface ToolbarStore {
14
14
  * a rejected batch leaves the previous table intact.
15
15
  */
16
16
  set(actions: ToolbarActionInput[]): void;
17
- /** Project the current table to serialisable display metadata — no `handler` leaks out. */
17
+ /** Project the current table to `{id,label}[]` — no `handler` leaks out. */
18
18
  list(): ToolbarAction[];
19
19
  /** Resolve the handler stored under `id`, or `undefined` if unknown. */
20
20
  getHandler(id: string): (() => void | Promise<void>) | undefined;
@@ -1,20 +1,3 @@
1
- function toPublicAction(action) {
2
- const projected = {
3
- id: action.id,
4
- label: action.label,
5
- };
6
- if (action.kind)
7
- projected.kind = action.kind;
8
- if (action.placement)
9
- projected.placement = action.placement;
10
- if (action.icon)
11
- projected.icon = action.icon;
12
- if (action.displayInitial)
13
- projected.displayInitial = action.displayInitial;
14
- if (action.avatarUrl)
15
- projected.avatarUrl = action.avatarUrl;
16
- return projected;
17
- }
18
1
  export function createToolbarStore() {
19
2
  let actions = [];
20
3
  return {
@@ -30,7 +13,7 @@ export function createToolbarStore() {
30
13
  actions = next.slice();
31
14
  },
32
15
  list() {
33
- return actions.map(toPublicAction);
16
+ return actions.map(({ id, label }) => ({ id, label }));
34
17
  },
35
18
  getHandler(id) {
36
19
  return actions.find((a) => a.id === id)?.handler;