@dimina-kit/devtools 0.4.0-dev.20260630094624 → 0.4.0-dev.20260701083540

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 (31) hide show
  1. package/dist/main/app/app.js +6 -0
  2. package/dist/main/index.bundle.js +114 -1
  3. package/dist/main/ipc/app-lifecycle.d.ts +32 -0
  4. package/dist/main/ipc/app-lifecycle.js +54 -0
  5. package/dist/main/ipc/bridge-router.js +95 -1
  6. package/dist/main/ipc/page-scroll.d.ts +14 -0
  7. package/dist/main/ipc/page-scroll.js +17 -0
  8. package/dist/preload/windows/main.cjs +200 -0
  9. package/dist/preload/windows/main.cjs.map +3 -3
  10. package/dist/renderer/assets/{index-BMwmmaSp.js → index-HE9Xr9bQ.js} +16 -13
  11. package/dist/renderer/assets/{input-BbWPVJlM.js → input-DR7bfyQE.js} +2 -2
  12. package/dist/renderer/assets/{ipc-transport-DTT-j7AD.js → ipc-transport-3Arg8jPC.js} +1 -1
  13. package/dist/renderer/assets/ipc-transport-Dt0jR7Ir.css +1 -0
  14. package/dist/renderer/assets/{popover-CjAdKdAo.js → popover-CPZX3MrT.js} +2 -2
  15. package/dist/renderer/assets/{select-RyHeTVsc.js → select-BxyPlVyJ.js} +2 -2
  16. package/dist/renderer/assets/{settings-DaR5vif_.js → settings-CG_OR03F.js} +2 -2
  17. package/dist/renderer/assets/{settings-api-DXYCjMQY.js → settings-api-CtPPJF-F.js} +2 -2
  18. package/dist/renderer/assets/{workbenchSettings-CwW0JEe6.js → workbenchSettings-ClqCBGOa.js} +2 -2
  19. package/dist/renderer/entries/main/index.html +6 -6
  20. package/dist/renderer/entries/popover/index.html +5 -5
  21. package/dist/renderer/entries/settings/index.html +5 -5
  22. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  23. package/dist/simulator/assets/{device-shell-BiFE9Otv.css → device-shell-CS1rqAQz.css} +1 -1
  24. package/dist/simulator/assets/device-shell-CT3LRnTB.js +2 -0
  25. package/dist/simulator/assets/{jsx-runtime-P6ZegaOf.js → jsx-runtime-CDK-o-S0.js} +2 -2
  26. package/dist/simulator/assets/simulator-BizhGGE3.js +10 -0
  27. package/dist/simulator/simulator.html +2 -2
  28. package/package.json +5 -5
  29. package/dist/renderer/assets/ipc-transport-BZ4gxQ4_.css +0 -1
  30. package/dist/simulator/assets/device-shell-CPCnCp1L.js +0 -2
  31. package/dist/simulator/assets/simulator-gMBWKDeO.js +0 -10
@@ -388,6 +388,12 @@ export async function createDevtoolsRuntime(config = {}) {
388
388
  registerSimulatorApi: (name, handler) => context.registry.add(toDisposable(context.simulatorApis.register(name, handler))),
389
389
  dispose: () => disposeContext(context),
390
390
  };
391
+ // Built-in simulator APIs: devtools-supplied wx.* implementations that run
392
+ // in the main process. Hosts can override any of these in their onSetup by
393
+ // re-registering the same name (last-write-wins on the Map).
394
+ instance.registerSimulatorApi('login', async () => {
395
+ return 'hello';
396
+ });
391
397
  if (config.onSetup) {
392
398
  await config.onSetup(instance);
393
399
  }
@@ -5627,6 +5627,52 @@ function findSimulatorWebContents() {
5627
5627
  return null;
5628
5628
  }
5629
5629
 
5630
+ // src/main/ipc/page-scroll.ts
5631
+ function buildPageScrollScript(params) {
5632
+ const rawTop = Number(params.scrollTop);
5633
+ const top = Number.isFinite(rawTop) ? rawTop : 0;
5634
+ const duration = params.duration === void 0 ? 300 : Number(params.duration);
5635
+ const behavior = Number.isFinite(duration) && duration > 0 ? "smooth" : "auto";
5636
+ return `window.scrollTo({ top: ${top}, left: 0, behavior: ${JSON.stringify(behavior)} })`;
5637
+ }
5638
+
5639
+ // src/main/ipc/app-lifecycle.ts
5640
+ function createAppLifecycleController() {
5641
+ const sessions = /* @__PURE__ */ new Map();
5642
+ return {
5643
+ register(appSessionId, event, callbackId) {
5644
+ if (callbackId === void 0 || callbackId === null) return;
5645
+ let events = sessions.get(appSessionId);
5646
+ if (!events) {
5647
+ events = /* @__PURE__ */ new Map();
5648
+ sessions.set(appSessionId, events);
5649
+ }
5650
+ let ids = events.get(event);
5651
+ if (!ids) {
5652
+ ids = /* @__PURE__ */ new Set();
5653
+ events.set(event, ids);
5654
+ }
5655
+ ids.add(callbackId);
5656
+ },
5657
+ unregister(appSessionId, event, callbackId) {
5658
+ const events = sessions.get(appSessionId);
5659
+ if (!events) return;
5660
+ if (callbackId === void 0 || callbackId === null) {
5661
+ events.delete(event);
5662
+ return;
5663
+ }
5664
+ events.get(event)?.delete(callbackId);
5665
+ },
5666
+ listeners(appSessionId, event) {
5667
+ const ids = sessions.get(appSessionId)?.get(event);
5668
+ return ids ? Array.from(ids) : [];
5669
+ },
5670
+ dispose(appSessionId) {
5671
+ sessions.delete(appSessionId);
5672
+ }
5673
+ };
5674
+ }
5675
+
5630
5676
  // src/main/ipc/bridge-router.ts
5631
5677
  function rewriteSourceMappingUrl(source, scriptUrl) {
5632
5678
  if (typeof source !== "string" || !source) return source;
@@ -5706,6 +5752,7 @@ function installBridgeRouter(ctx) {
5706
5752
  },
5707
5753
  connections: ctx.connections,
5708
5754
  debugTap: createDebugTap({ enabled: resolveDebugTapEnabled() }),
5755
+ appLifecycle: createAppLifecycleController(),
5709
5756
  evictAppDataBridges: (ap) => {
5710
5757
  if (!ctx.appData) return;
5711
5758
  for (const page of ap.pages.values()) ctx.appData.evictBridge(ap.appId, page.bridgeId);
@@ -5723,6 +5770,7 @@ function installBridgeRouter(ctx) {
5723
5770
  ctx.registry.add(() => clearTimeout(warmTimer));
5724
5771
  ctx.registry.add(() => state.pool?.dispose());
5725
5772
  }
5773
+ installAppLifecycleDriver(ctx, state);
5726
5774
  installResourceProtocolHandlers(ctx, state);
5727
5775
  let currentDevice = null;
5728
5776
  const onNativeHostQuery = (event) => {
@@ -6319,9 +6367,15 @@ function handleContainerMsg(ap, page, msg, ctx, state) {
6319
6367
  case "invokeAPI":
6320
6368
  void handleSimulatorApi(state, ap, page, msg.body, ctx);
6321
6369
  break;
6322
- case "serviceHostError":
6370
+ case "serviceHostError": {
6323
6371
  console.warn("[bridge-router] service host error:", msg.body);
6372
+ const errBody = msg.body;
6373
+ const errArg = errBody?.message ?? msg.body;
6374
+ for (const id of state.appLifecycle.listeners(ap.appSessionId, "onError")) {
6375
+ sendCallback(ap, id, errArg);
6376
+ }
6324
6377
  break;
6378
+ }
6325
6379
  case "consoleLog":
6326
6380
  ctx.guestConsole?.emit(msg.body);
6327
6381
  break;
@@ -6353,6 +6407,40 @@ var TAB_ACTION_NAMES = /* @__PURE__ */ new Set([
6353
6407
  "showTabBarRedDot",
6354
6408
  "hideTabBarRedDot"
6355
6409
  ]);
6410
+ function installAppLifecycleDriver(ctx, state) {
6411
+ const win = ctx.windows?.mainWindow;
6412
+ if (!win || typeof win.on !== "function") return;
6413
+ const emit = (serviceEvent, listenerEvent) => {
6414
+ for (const ap of state.appSessions.values()) {
6415
+ forwardToService(ap, { type: serviceEvent, target: "service", body: {} });
6416
+ for (const id of state.appLifecycle.listeners(ap.appSessionId, listenerEvent)) {
6417
+ sendCallback(ap, id, {});
6418
+ }
6419
+ }
6420
+ };
6421
+ const onHide = () => emit("appHide", "onAppHide");
6422
+ const onShow = () => emit("appShow", "onAppShow");
6423
+ win.on("hide", onHide);
6424
+ win.on("minimize", onHide);
6425
+ win.on("show", onShow);
6426
+ win.on("restore", onShow);
6427
+ ctx.registry.add(() => {
6428
+ win.off("hide", onHide);
6429
+ win.off("minimize", onHide);
6430
+ win.off("show", onShow);
6431
+ win.off("restore", onShow);
6432
+ });
6433
+ }
6434
+ var APP_LIFECYCLE_REGISTER = {
6435
+ onAppShow: "onAppShow",
6436
+ onAppHide: "onAppHide",
6437
+ onError: "onError"
6438
+ };
6439
+ var APP_LIFECYCLE_UNREGISTER = {
6440
+ offAppShow: "onAppShow",
6441
+ offAppHide: "onAppHide",
6442
+ offError: "onError"
6443
+ };
6356
6444
  async function handleSimulatorApi(state, ap, page, body, ctx) {
6357
6445
  const name = String(body.name ?? "");
6358
6446
  const params = normalizeParams(body.params);
@@ -6403,6 +6491,27 @@ async function handleSimulatorApi(state, ap, page, body, ctx) {
6403
6491
  }
6404
6492
  return;
6405
6493
  }
6494
+ const lifecycleRegister = APP_LIFECYCLE_REGISTER[name];
6495
+ if (lifecycleRegister) {
6496
+ state.appLifecycle.register(ap.appSessionId, lifecycleRegister, params.success);
6497
+ return;
6498
+ }
6499
+ const lifecycleUnregister = APP_LIFECYCLE_UNREGISTER[name];
6500
+ if (lifecycleUnregister) {
6501
+ state.appLifecycle.unregister(ap.appSessionId, lifecycleUnregister, params.success);
6502
+ return;
6503
+ }
6504
+ if (name === "pageScrollTo") {
6505
+ const renderWc = page.renderWc;
6506
+ if (renderWc && !renderWc.isDestroyed()) {
6507
+ void renderWc.executeJavaScript(buildPageScrollScript(params)).catch(() => {
6508
+ });
6509
+ }
6510
+ const successResult = { errMsg: "pageScrollTo:ok" };
6511
+ sendCallback(ap, params.success, successResult);
6512
+ sendCallback(ap, params.complete, successResult);
6513
+ return;
6514
+ }
6406
6515
  if (ctx.storageApi && STORAGE_API_NAMES.has(name)) {
6407
6516
  try {
6408
6517
  const result = await ctx.storageApi.invoke(ap.appId, name, params);
@@ -6627,6 +6736,7 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
6627
6736
  const ap = state.appSessions.get(appSessionId);
6628
6737
  if (!ap) return;
6629
6738
  state.appSessions.delete(appSessionId);
6739
+ state.appLifecycle.dispose(appSessionId);
6630
6740
  state.evictAppDataBridges(ap);
6631
6741
  for (const [requestId, pending] of state.pendingApiCalls) {
6632
6742
  if (pending.appSessionId !== appSessionId) continue;
@@ -10292,6 +10402,9 @@ async function createDevtoolsRuntime(config = {}) {
10292
10402
  registerSimulatorApi: (name, handler) => context.registry.add(toDisposable8(context.simulatorApis.register(name, handler))),
10293
10403
  dispose: () => disposeContext(context)
10294
10404
  };
10405
+ instance.registerSimulatorApi("login", async () => {
10406
+ return "hello";
10407
+ });
10295
10408
  if (config.onSetup) {
10296
10409
  await config.onSetup(instance);
10297
10410
  }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Per-app-session registry for the app-level lifecycle listeners registered
3
+ * from the mini-program service layer: `wx.onAppShow` / `onAppHide` / `onError`
4
+ * (and their `off*` removers).
5
+ *
6
+ * These arrive as keep subscriptions — `wx.onAppShow(cb)` encodes `cb` as a
7
+ * persistent callback id in `params.success` (service `callback.store(fn,
8
+ * keep=true)`), so the router stores the id here and re-fires it via
9
+ * `sendCallback` on every app foreground / background / error.
10
+ *
11
+ * `wx.offAppShow(cb)` re-encodes the SAME `cb`: with `keep=true`,
12
+ * `callback.store` dedups by function and returns the original evtId, so `off*`
13
+ * carries that id and removes exactly that listener. `wx.offAppShow()` with no
14
+ * argument carries no id and clears every listener of the event (WeChat
15
+ * contract).
16
+ */
17
+ export type AppLifecycleEvent = 'onAppShow' | 'onAppHide' | 'onError';
18
+ export interface AppLifecycleController {
19
+ /** Store a keep callback id for an event. Null/undefined ids are ignored. */
20
+ register(appSessionId: string, event: AppLifecycleEvent, callbackId: unknown): void;
21
+ /**
22
+ * Remove a listener. With `callbackId`, removes only that id; without one,
23
+ * clears every listener of the event for the session.
24
+ */
25
+ unregister(appSessionId: string, event: AppLifecycleEvent, callbackId?: unknown): void;
26
+ /** Snapshot of the registered callback ids (empty for unknown session/event). */
27
+ listeners(appSessionId: string, event: AppLifecycleEvent): unknown[];
28
+ /** Drop all listeners for a torn-down session. */
29
+ dispose(appSessionId: string): void;
30
+ }
31
+ export declare function createAppLifecycleController(): AppLifecycleController;
32
+ //# sourceMappingURL=app-lifecycle.d.ts.map
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Per-app-session registry for the app-level lifecycle listeners registered
3
+ * from the mini-program service layer: `wx.onAppShow` / `onAppHide` / `onError`
4
+ * (and their `off*` removers).
5
+ *
6
+ * These arrive as keep subscriptions — `wx.onAppShow(cb)` encodes `cb` as a
7
+ * persistent callback id in `params.success` (service `callback.store(fn,
8
+ * keep=true)`), so the router stores the id here and re-fires it via
9
+ * `sendCallback` on every app foreground / background / error.
10
+ *
11
+ * `wx.offAppShow(cb)` re-encodes the SAME `cb`: with `keep=true`,
12
+ * `callback.store` dedups by function and returns the original evtId, so `off*`
13
+ * carries that id and removes exactly that listener. `wx.offAppShow()` with no
14
+ * argument carries no id and clears every listener of the event (WeChat
15
+ * contract).
16
+ */
17
+ export function createAppLifecycleController() {
18
+ const sessions = new Map();
19
+ return {
20
+ register(appSessionId, event, callbackId) {
21
+ if (callbackId === undefined || callbackId === null)
22
+ return;
23
+ let events = sessions.get(appSessionId);
24
+ if (!events) {
25
+ events = new Map();
26
+ sessions.set(appSessionId, events);
27
+ }
28
+ let ids = events.get(event);
29
+ if (!ids) {
30
+ ids = new Set();
31
+ events.set(event, ids);
32
+ }
33
+ ids.add(callbackId);
34
+ },
35
+ unregister(appSessionId, event, callbackId) {
36
+ const events = sessions.get(appSessionId);
37
+ if (!events)
38
+ return;
39
+ if (callbackId === undefined || callbackId === null) {
40
+ events.delete(event);
41
+ return;
42
+ }
43
+ events.get(event)?.delete(callbackId);
44
+ },
45
+ listeners(appSessionId, event) {
46
+ const ids = sessions.get(appSessionId)?.get(event);
47
+ return ids ? Array.from(ids) : [];
48
+ },
49
+ dispose(appSessionId) {
50
+ sessions.delete(appSessionId);
51
+ },
52
+ };
53
+ }
54
+ //# sourceMappingURL=app-lifecycle.js.map
@@ -11,6 +11,8 @@ import { ServiceHostPool } from '../services/service-host-pool/pool.js';
11
11
  import { registerMiniappSessionConfigurator, SHARED_MINIAPP_PARTITION, } from '../services/views/miniapp-partition.js';
12
12
  import { createConsoleForwarder } from '../services/console-forward/index.js';
13
13
  import { STORAGE_API_NAMES } from '../services/simulator-storage/index.js';
14
+ import { buildPageScrollScript } from './page-scroll.js';
15
+ import { createAppLifecycleController, } from './app-lifecycle.js';
14
16
  // The compiled `logic.js` ships a RELATIVE `//# sourceMappingURL=logic.js.map`.
15
17
  // `injectLogicBundle` loads it via `executeJavaScript`, which gives the injected
16
18
  // script no base URL of its own — so DevTools resolves that relative map against
@@ -152,6 +154,7 @@ export function installBridgeRouter(ctx) {
152
154
  emitRenderEvent: () => { },
153
155
  connections: ctx.connections,
154
156
  debugTap: createDebugTap({ enabled: resolveDebugTapEnabled() }),
157
+ appLifecycle: createAppLifecycleController(),
155
158
  evictAppDataBridges: (ap) => {
156
159
  if (!ctx.appData)
157
160
  return;
@@ -180,6 +183,7 @@ export function installBridgeRouter(ctx) {
180
183
  ctx.registry.add(() => clearTimeout(warmTimer));
181
184
  ctx.registry.add(() => state.pool?.dispose());
182
185
  }
186
+ installAppLifecycleDriver(ctx, state);
183
187
  installResourceProtocolHandlers(ctx, state);
184
188
  // The currently-selected device (renderer toolbar). Cached here so it can ride
185
189
  // the NATIVE_HOST_ENABLED reply (race-free DeviceShell init) and so the
@@ -951,9 +955,20 @@ function handleContainerMsg(ap, page, msg, ctx, state) {
951
955
  case 'invokeAPI':
952
956
  void handleSimulatorApi(state, ap, page, msg.body, ctx);
953
957
  break;
954
- case 'serviceHostError':
958
+ case 'serviceHostError': {
955
959
  console.warn('[bridge-router] service host error:', msg.body);
960
+ // Forward the error to every registered `wx.onError` listener. (The dimina
961
+ // service runtime's App lifecycle is only onLaunch/onShow/onHide, so it
962
+ // does not dispatch `App.onError` — `wx.onError` is the supported path.)
963
+ // The service preload's `deliver` try/catch already reports errors thrown
964
+ // during lifecycle/event dispatch here.
965
+ const errBody = msg.body;
966
+ const errArg = errBody?.message ?? msg.body;
967
+ for (const id of state.appLifecycle.listeners(ap.appSessionId, 'onError')) {
968
+ sendCallback(ap, id, errArg);
969
+ }
956
970
  break;
971
+ }
957
972
  case 'consoleLog':
958
973
  // Native-host console capture: the render-host / service-host guest preloads
959
974
  // monkeypatch console.* and post each entry here (one case handles both —
@@ -991,6 +1006,54 @@ const TAB_ACTION_NAMES = new Set([
991
1006
  'showTabBarRedDot',
992
1007
  'hideTabBarRedDot',
993
1008
  ]);
1009
+ /**
1010
+ * Drive app foreground/background from the main window's visibility. Minimizing
1011
+ * or hiding the window backgrounds the mini-program (App.onHide + wx.onAppHide);
1012
+ * restoring or showing it foregrounds it (App.onShow + wx.onAppShow). Focus/blur
1013
+ * is deliberately NOT used — clicking a DevTools panel would falsely fire it.
1014
+ * The initial App.onShow fires once at spawn (service `invokeSomeLifecycle`), so
1015
+ * the driver only handles subsequent transitions.
1016
+ */
1017
+ function installAppLifecycleDriver(ctx, state) {
1018
+ const win = ctx.windows?.mainWindow;
1019
+ // Guard against a headless/stub mainWindow (unit tests) that isn't a real
1020
+ // event-emitting BrowserWindow.
1021
+ if (!win || typeof win.on !== 'function')
1022
+ return;
1023
+ const emit = (serviceEvent, listenerEvent) => {
1024
+ for (const ap of state.appSessions.values()) {
1025
+ // App.onShow / onHide: the service runtime already listens for these.
1026
+ forwardToService(ap, { type: serviceEvent, target: 'service', body: {} });
1027
+ // wx.onAppShow / onAppHide imperative listeners.
1028
+ for (const id of state.appLifecycle.listeners(ap.appSessionId, listenerEvent)) {
1029
+ sendCallback(ap, id, {});
1030
+ }
1031
+ }
1032
+ };
1033
+ const onHide = () => emit('appHide', 'onAppHide');
1034
+ const onShow = () => emit('appShow', 'onAppShow');
1035
+ win.on('hide', onHide);
1036
+ win.on('minimize', onHide);
1037
+ win.on('show', onShow);
1038
+ win.on('restore', onShow);
1039
+ ctx.registry.add(() => {
1040
+ win.off('hide', onHide);
1041
+ win.off('minimize', onHide);
1042
+ win.off('show', onShow);
1043
+ win.off('restore', onShow);
1044
+ });
1045
+ }
1046
+ // wx app-event API name → the lifecycle event whose listeners it (de)registers.
1047
+ const APP_LIFECYCLE_REGISTER = {
1048
+ onAppShow: 'onAppShow',
1049
+ onAppHide: 'onAppHide',
1050
+ onError: 'onError',
1051
+ };
1052
+ const APP_LIFECYCLE_UNREGISTER = {
1053
+ offAppShow: 'onAppShow',
1054
+ offAppHide: 'onAppHide',
1055
+ offError: 'onError',
1056
+ };
994
1057
  async function handleSimulatorApi(state, ap, page, body, ctx) {
995
1058
  const name = String(body.name ?? '');
996
1059
  const params = normalizeParams(body.params);
@@ -1043,6 +1106,36 @@ async function handleSimulatorApi(state, ap, page, body, ctx) {
1043
1106
  }
1044
1107
  return;
1045
1108
  }
1109
+ // App-level lifecycle listeners (wx.onAppShow / onAppHide / onError + off*).
1110
+ // The service encodes the listener as a keep callback id in `params.success`;
1111
+ // we store it and re-fire on main-window foreground/background (the driver in
1112
+ // installBridgeRouter) and on serviceHostError. `off*` clears the event's
1113
+ // listeners for the session.
1114
+ const lifecycleRegister = APP_LIFECYCLE_REGISTER[name];
1115
+ if (lifecycleRegister) {
1116
+ state.appLifecycle.register(ap.appSessionId, lifecycleRegister, params.success);
1117
+ return;
1118
+ }
1119
+ const lifecycleUnregister = APP_LIFECYCLE_UNREGISTER[name];
1120
+ if (lifecycleUnregister) {
1121
+ // off(cb) carries the same evtId as the original on(cb) → removes just that
1122
+ // listener; off() with no callback carries no id → clears the whole event.
1123
+ state.appLifecycle.unregister(ap.appSessionId, lifecycleUnregister, params.success);
1124
+ return;
1125
+ }
1126
+ // pageScrollTo acts on the page's render guest (scroll its document), which
1127
+ // only the main process can reach — run the scroll script in the invoking
1128
+ // page's render webContents rather than forwarding to the simulator.
1129
+ if (name === 'pageScrollTo') {
1130
+ const renderWc = page.renderWc;
1131
+ if (renderWc && !renderWc.isDestroyed()) {
1132
+ void renderWc.executeJavaScript(buildPageScrollScript(params)).catch(() => { });
1133
+ }
1134
+ const successResult = { errMsg: 'pageScrollTo:ok' };
1135
+ sendCallback(ap, params.success, successResult);
1136
+ sendCallback(ap, params.complete, successResult);
1137
+ return;
1138
+ }
1046
1139
  // Native-host storage unification: route async wx.setStorage/getStorage/etc.
1047
1140
  // to the service-host window's file:// store (the same store the *Sync APIs +
1048
1141
  // the Storage panel use), instead of forwarding to the simulator guest's
@@ -1335,6 +1428,7 @@ async function disposeAppSession(state, appSessionId, opts = {}) {
1335
1428
  if (!ap)
1336
1429
  return;
1337
1430
  state.appSessions.delete(appSessionId);
1431
+ state.appLifecycle.dispose(appSessionId);
1338
1432
  // Evict AppData bridges FIRST — eviction enumerates `ap.pages`, which the
1339
1433
  // page teardown below progressively empties (and finally clears).
1340
1434
  state.evictAppDataBridges(ap);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Build the script that `wx.pageScrollTo` runs inside the page's render guest.
3
+ *
4
+ * The mini-program page scrolls the whole render document (the native iOS impl
5
+ * scrolls the WKWebView's scrollView; Android scrolls the page view), so the
6
+ * simulator scrolls `window`. `duration` is best-effort: a positive duration
7
+ * maps to smooth scrolling (the browser owns the easing — the exact ms is not
8
+ * controllable), and `0` jumps instantly.
9
+ */
10
+ export declare function buildPageScrollScript(params: {
11
+ scrollTop?: unknown;
12
+ duration?: unknown;
13
+ }): string;
14
+ //# sourceMappingURL=page-scroll.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Build the script that `wx.pageScrollTo` runs inside the page's render guest.
3
+ *
4
+ * The mini-program page scrolls the whole render document (the native iOS impl
5
+ * scrolls the WKWebView's scrollView; Android scrolls the page view), so the
6
+ * simulator scrolls `window`. `duration` is best-effort: a positive duration
7
+ * maps to smooth scrolling (the browser owns the easing — the exact ms is not
8
+ * controllable), and `0` jumps instantly.
9
+ */
10
+ export function buildPageScrollScript(params) {
11
+ const rawTop = Number(params.scrollTop);
12
+ const top = Number.isFinite(rawTop) ? rawTop : 0;
13
+ const duration = params.duration === undefined ? 300 : Number(params.duration);
14
+ const behavior = Number.isFinite(duration) && duration > 0 ? 'smooth' : 'auto';
15
+ return `window.scrollTo({ top: ${top}, left: 0, behavior: ${JSON.stringify(behavior)} })`;
16
+ }
17
+ //# sourceMappingURL=page-scroll.js.map
@@ -327,6 +327,68 @@ function vibrateLong({ success, complete } = {}) {
327
327
  onComplete?.();
328
328
  }
329
329
  var scanCode = notSupportedApi("scanCode");
330
+ function getClipboardData({ success, fail, complete } = {}) {
331
+ const { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete });
332
+ return navigator.clipboard.readText().then(
333
+ (data) => {
334
+ onSuccess?.({ data, errMsg: "getClipboardData:ok" });
335
+ onComplete?.();
336
+ },
337
+ (error) => {
338
+ onFail?.({ errMsg: `getClipboardData:fail ${error?.message ?? error}` });
339
+ onComplete?.();
340
+ }
341
+ );
342
+ }
343
+ function setClipboardData({ data, success, fail, complete } = {}) {
344
+ const { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete });
345
+ if (typeof data !== "string") {
346
+ onFail?.({ errMsg: "setClipboardData:fail data is required" });
347
+ onComplete?.();
348
+ return Promise.resolve();
349
+ }
350
+ return navigator.clipboard.writeText(data).then(
351
+ () => {
352
+ onSuccess?.({ errMsg: "setClipboardData:ok" });
353
+ onComplete?.();
354
+ },
355
+ (error) => {
356
+ onFail?.({ errMsg: `setClipboardData:fail ${error?.message ?? error}` });
357
+ onComplete?.();
358
+ }
359
+ );
360
+ }
361
+ function mapEffectiveType(effectiveType) {
362
+ switch (effectiveType) {
363
+ case "4g":
364
+ return "4g";
365
+ case "3g":
366
+ return "3g";
367
+ case "2g":
368
+ case "slow-2g":
369
+ return "2g";
370
+ default:
371
+ return "unknown";
372
+ }
373
+ }
374
+ function resolveNetworkType(info) {
375
+ if (!info.onLine) return "none";
376
+ if (info.type === "wifi" || info.type === "ethernet") return "wifi";
377
+ if (info.type === "cellular") return mapEffectiveType(info.effectiveType);
378
+ if (info.effectiveType) return mapEffectiveType(info.effectiveType);
379
+ return "wifi";
380
+ }
381
+ function getNetworkType({ success, complete } = {}) {
382
+ const { onSuccess, onComplete } = bindCallbacks(this, { success, complete });
383
+ const connection = navigator.connection;
384
+ const networkType = resolveNetworkType({
385
+ onLine: navigator.onLine,
386
+ type: connection?.type,
387
+ effectiveType: connection?.effectiveType
388
+ });
389
+ onSuccess?.({ networkType, errMsg: "getNetworkType:ok" });
390
+ onComplete?.();
391
+ }
330
392
 
331
393
  // src/simulator/event-bridge.ts
332
394
  function bindDomEvents(target, eventMap, fire, buildPayload) {
@@ -1754,6 +1816,134 @@ function uploadFileAbort({ uploadId } = {}) {
1754
1816
  }
1755
1817
  }
1756
1818
 
1819
+ // src/simulator/ui-overlay-bus.ts
1820
+ var UiOverlayBus = class {
1821
+ state = { toast: null, dialog: null };
1822
+ listeners = /* @__PURE__ */ new Set();
1823
+ getState() {
1824
+ return this.state;
1825
+ }
1826
+ subscribe(listener) {
1827
+ this.listeners.add(listener);
1828
+ return () => {
1829
+ this.listeners.delete(listener);
1830
+ };
1831
+ }
1832
+ showToast(toast) {
1833
+ this.set({ ...this.state, toast });
1834
+ }
1835
+ hideToast() {
1836
+ this.set({ ...this.state, toast: null });
1837
+ }
1838
+ /**
1839
+ * Clear `toast` only if it is still the active one. The toast auto-dismiss
1840
+ * timer holds a reference to the toast it scheduled; a newer showToast may
1841
+ * have replaced it in the meantime, and a blind `hideToast()` from the stale
1842
+ * timer would wrongly clear the new toast.
1843
+ */
1844
+ dismissToast(toast) {
1845
+ if (this.state.toast === toast) this.hideToast();
1846
+ }
1847
+ showDialog(dialog) {
1848
+ this.set({ ...this.state, dialog });
1849
+ }
1850
+ hideDialog() {
1851
+ this.set({ ...this.state, dialog: null });
1852
+ }
1853
+ set(next) {
1854
+ this.state = next;
1855
+ for (const listener of this.listeners) listener(next);
1856
+ }
1857
+ };
1858
+ var uiOverlayBus = new UiOverlayBus();
1859
+
1860
+ // src/simulator/simulator-api-ui.ts
1861
+ function showToast(opts = {}) {
1862
+ const { onSuccess, onComplete } = bindCallbacks(this, opts);
1863
+ uiOverlayBus.showToast({
1864
+ title: opts.title ?? "",
1865
+ icon: opts.icon ?? "success",
1866
+ image: opts.image,
1867
+ duration: opts.duration ?? 1500,
1868
+ mask: opts.mask ?? false
1869
+ });
1870
+ onSuccess?.({ errMsg: "showToast:ok" });
1871
+ onComplete?.();
1872
+ }
1873
+ function hideToast(opts = {}) {
1874
+ const { onSuccess, onComplete } = bindCallbacks(this, opts);
1875
+ uiOverlayBus.hideToast();
1876
+ onSuccess?.({ errMsg: "hideToast:ok" });
1877
+ onComplete?.();
1878
+ }
1879
+ function showLoading(opts = {}) {
1880
+ const { onSuccess, onComplete } = bindCallbacks(this, opts);
1881
+ uiOverlayBus.showToast({
1882
+ title: opts.title ?? "",
1883
+ icon: "loading",
1884
+ duration: Infinity,
1885
+ mask: opts.mask ?? false
1886
+ });
1887
+ onSuccess?.({ errMsg: "showLoading:ok" });
1888
+ onComplete?.();
1889
+ }
1890
+ function hideLoading(opts = {}) {
1891
+ const { onSuccess, onComplete } = bindCallbacks(this, opts);
1892
+ uiOverlayBus.hideToast();
1893
+ onSuccess?.({ errMsg: "hideLoading:ok" });
1894
+ onComplete?.();
1895
+ }
1896
+ function showModal(opts = {}) {
1897
+ const { onSuccess, onComplete } = bindCallbacks(this, opts);
1898
+ const editable = opts.editable ?? false;
1899
+ let settled = false;
1900
+ uiOverlayBus.showDialog({
1901
+ kind: "modal",
1902
+ title: opts.title ?? "",
1903
+ content: opts.content ?? "",
1904
+ showCancel: opts.showCancel ?? true,
1905
+ cancelText: opts.cancelText ?? "\u53D6\u6D88",
1906
+ cancelColor: opts.cancelColor ?? "#000000",
1907
+ confirmText: opts.confirmText ?? "\u786E\u5B9A",
1908
+ confirmColor: opts.confirmColor ?? "#576B95",
1909
+ editable,
1910
+ placeholderText: opts.placeholderText ?? "",
1911
+ onResult: (confirmed, content) => {
1912
+ if (settled) return;
1913
+ settled = true;
1914
+ uiOverlayBus.hideDialog();
1915
+ const result = {
1916
+ confirm: confirmed,
1917
+ cancel: !confirmed,
1918
+ errMsg: "showModal:ok"
1919
+ };
1920
+ if (editable) result.content = content ?? "";
1921
+ onSuccess?.(result);
1922
+ onComplete?.();
1923
+ }
1924
+ });
1925
+ }
1926
+ function showActionSheet(opts = {}) {
1927
+ const { onSuccess, onFail, onComplete } = bindCallbacks(this, opts);
1928
+ let settled = false;
1929
+ uiOverlayBus.showDialog({
1930
+ kind: "actionSheet",
1931
+ itemList: opts.itemList ?? [],
1932
+ itemColor: opts.itemColor ?? "#000000",
1933
+ onSelect: (index) => {
1934
+ if (settled) return;
1935
+ settled = true;
1936
+ uiOverlayBus.hideDialog();
1937
+ if (index === -1) {
1938
+ onFail?.({ errMsg: "showActionSheet:fail cancel" });
1939
+ } else {
1940
+ onSuccess?.({ tapIndex: index, errMsg: "showActionSheet:ok" });
1941
+ }
1942
+ onComplete?.();
1943
+ }
1944
+ });
1945
+ }
1946
+
1757
1947
  // src/simulator/simulator-api.ts
1758
1948
  function canIUse({ success, complete }) {
1759
1949
  const { onSuccess, onComplete } = bindCallbacks(this, { success, complete });
@@ -1872,6 +2062,13 @@ var simulatorApis = {
1872
2062
  getSystemInfoSync,
1873
2063
  getWindowInfo,
1874
2064
  getSystemSetting,
2065
+ // UI: interaction
2066
+ showToast,
2067
+ hideToast,
2068
+ showLoading,
2069
+ hideLoading,
2070
+ showModal,
2071
+ showActionSheet,
1875
2072
  // Network
1876
2073
  downloadFile,
1877
2074
  uploadFile,
@@ -1899,6 +2096,9 @@ var simulatorApis = {
1899
2096
  vibrateShort,
1900
2097
  vibrateLong,
1901
2098
  scanCode,
2099
+ getClipboardData,
2100
+ setClipboardData,
2101
+ getNetworkType,
1902
2102
  // Media: Image
1903
2103
  chooseImage,
1904
2104
  previewImage,