@dimina-kit/devtools 0.4.0-dev.20260630094624 → 0.4.0-dev.20260630123203
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main/index.bundle.js +111 -1
- package/dist/main/ipc/app-lifecycle.d.ts +32 -0
- package/dist/main/ipc/app-lifecycle.js +54 -0
- package/dist/main/ipc/bridge-router.js +95 -1
- package/dist/main/ipc/page-scroll.d.ts +14 -0
- package/dist/main/ipc/page-scroll.js +17 -0
- package/dist/preload/windows/main.cjs +200 -0
- package/dist/preload/windows/main.cjs.map +3 -3
- package/dist/simulator/assets/{device-shell-BiFE9Otv.css → device-shell-CS1rqAQz.css} +1 -1
- package/dist/simulator/assets/device-shell-H3Bq5NgA.js +2 -0
- package/dist/simulator/assets/{jsx-runtime-P6ZegaOf.js → jsx-runtime-CDK-o-S0.js} +2 -2
- package/dist/simulator/assets/simulator-CYPhufF2.js +10 -0
- package/dist/simulator/simulator.html +2 -2
- package/package.json +5 -5
- package/dist/simulator/assets/device-shell-CPCnCp1L.js +0 -2
- package/dist/simulator/assets/simulator-gMBWKDeO.js +0 -10
|
@@ -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;
|
|
@@ -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,
|