@weapp-vite/miniprogram-automator 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -4
- package/dist/index.d.mts +6 -1
- package/dist/index.mjs +63 -4304
- package/dist/{launch-IFPMxQYb.mjs → launch-Bd3TZy1I.mjs} +1660 -61
- package/package.json +5 -4
|
@@ -28,6 +28,8 @@ function createAppInstance(definition) {
|
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region ../../mpcore/packages/simulator/src/runtime/componentInstance.ts
|
|
31
|
+
const ARRAY_INDEX_PATH_RE$1 = /\[(\d+)\]/g;
|
|
32
|
+
const ARRAY_INDEX_SEGMENT_RE$1 = /^\d+$/;
|
|
31
33
|
function bindFunction$1(target, key, value) {
|
|
32
34
|
if (typeof value !== "function") {
|
|
33
35
|
target[key] = value;
|
|
@@ -44,10 +46,10 @@ function cloneValue$1(value) {
|
|
|
44
46
|
return value;
|
|
45
47
|
}
|
|
46
48
|
function parseDataPath$1(path) {
|
|
47
|
-
return path.replace(
|
|
49
|
+
return path.replace(ARRAY_INDEX_PATH_RE$1, ".$1").split(".").map((segment) => segment.trim()).filter(Boolean);
|
|
48
50
|
}
|
|
49
51
|
function isArrayIndexSegment$1(segment) {
|
|
50
|
-
return
|
|
52
|
+
return ARRAY_INDEX_SEGMENT_RE$1.test(segment);
|
|
51
53
|
}
|
|
52
54
|
function createContainerByNextSegment$1(nextSegment) {
|
|
53
55
|
return isArrayIndexSegment$1(nextSegment ?? "") ? [] : {};
|
|
@@ -64,7 +66,7 @@ function assignByPath$1(target, path, value) {
|
|
|
64
66
|
if (!next || typeof next !== "object") current[normalizedSegment] = createContainerByNextSegment$1(nextSegment);
|
|
65
67
|
current = current[normalizedSegment];
|
|
66
68
|
}
|
|
67
|
-
const leafSegment = segments
|
|
69
|
+
const leafSegment = segments.at(-1);
|
|
68
70
|
const normalizedLeafSegment = isArrayIndexSegment$1(leafSegment) ? Number(leafSegment) : leafSegment;
|
|
69
71
|
current[normalizedLeafSegment] = value;
|
|
70
72
|
}
|
|
@@ -184,7 +186,7 @@ function resolveInitialProperties(definition, properties) {
|
|
|
184
186
|
const resolved = {};
|
|
185
187
|
const propOptions = definition.properties;
|
|
186
188
|
if (propOptions && typeof propOptions === "object" && !Array.isArray(propOptions)) for (const [key, option] of Object.entries(propOptions)) {
|
|
187
|
-
if (Object.
|
|
189
|
+
if (Object.hasOwn(properties, key)) {
|
|
188
190
|
resolved[key] = coerceComponentPropertyValue(properties[key], option);
|
|
189
191
|
continue;
|
|
190
192
|
}
|
|
@@ -220,7 +222,7 @@ function resolveObservedValue(instance, pattern) {
|
|
|
220
222
|
const segments = parseDataPath$1(pattern);
|
|
221
223
|
if (segments.length === 0) return;
|
|
222
224
|
const [rootSegment, ...restSegments] = segments;
|
|
223
|
-
let current = (Object.
|
|
225
|
+
let current = (Object.hasOwn(instance.properties, rootSegment) ? instance.properties : instance.data)?.[rootSegment];
|
|
224
226
|
for (const segment of restSegments) {
|
|
225
227
|
const normalizedSegment = isArrayIndexSegment$1(segment) ? Number(segment) : segment;
|
|
226
228
|
current = current?.[normalizedSegment];
|
|
@@ -2853,7 +2855,7 @@ const STRUCTURAL_ATTRS = [
|
|
|
2853
2855
|
"wx:key"
|
|
2854
2856
|
];
|
|
2855
2857
|
const WX_ELSE_ATTRS = new Set(["wx:elif", "wx:else"]);
|
|
2856
|
-
const DATASET_NAME_RE$
|
|
2858
|
+
const DATASET_NAME_RE$3 = /-([a-z])/g;
|
|
2857
2859
|
const BRACKET_INDEX_RE = /\[(\d+)\]/g;
|
|
2858
2860
|
const CLASS_SPLIT_RE = /\s+/;
|
|
2859
2861
|
const JS_FILE_RE = /\.js$/;
|
|
@@ -2861,14 +2863,14 @@ function isMustacheOnly(value) {
|
|
|
2861
2863
|
const trimmed = value.trim();
|
|
2862
2864
|
return trimmed.startsWith("{{") && trimmed.endsWith("}}") && !trimmed.includes("{{", 2);
|
|
2863
2865
|
}
|
|
2864
|
-
function toDatasetKey$
|
|
2865
|
-
return attributeName.slice(5).replace(DATASET_NAME_RE$
|
|
2866
|
+
function toDatasetKey$3(attributeName) {
|
|
2867
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE$3, (_match, char) => char.toUpperCase());
|
|
2866
2868
|
}
|
|
2867
|
-
function collectDataset$
|
|
2869
|
+
function collectDataset$3(node) {
|
|
2868
2870
|
const dataset = {};
|
|
2869
2871
|
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
2870
2872
|
if (!key.startsWith("data-") || key === "data-sim-scope" || key === "data-sim-tap" || key === "data-sim-component") continue;
|
|
2871
|
-
dataset[toDatasetKey$
|
|
2873
|
+
dataset[toDatasetKey$3(key)] = String(value);
|
|
2872
2874
|
}
|
|
2873
2875
|
return dataset;
|
|
2874
2876
|
}
|
|
@@ -3006,7 +3008,7 @@ function collectComponentEventBindings(hostNode) {
|
|
|
3006
3008
|
return eventBindings;
|
|
3007
3009
|
}
|
|
3008
3010
|
function buildComponentTrigger(componentScopeId, context, hostNode) {
|
|
3009
|
-
const hostDataset = collectDataset$
|
|
3011
|
+
const hostDataset = collectDataset$3(hostNode);
|
|
3010
3012
|
const hostId = hostNode.attribs?.id ?? "";
|
|
3011
3013
|
return (instance, eventName, detail, triggerOptions) => {
|
|
3012
3014
|
const interactionTarget = instance.__lastInteractionEvent__?.target;
|
|
@@ -3187,6 +3189,8 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
|
|
|
3187
3189
|
properties: nextProperties,
|
|
3188
3190
|
triggerEvent: buildComponentTrigger(componentScopeId, context, clonedNode)
|
|
3189
3191
|
});
|
|
3192
|
+
componentInstance.createIntersectionObserver = (options) => context.session.createIntersectionObserver(componentInstance, options);
|
|
3193
|
+
componentInstance.createMediaQueryObserver = () => context.session.createMediaQueryObserver(componentInstance);
|
|
3190
3194
|
componentInstance.selectComponent = (selector) => context.session.selectComponentWithin(componentScopeId, selector);
|
|
3191
3195
|
componentInstance.selectAllComponents = (selector) => context.session.selectAllComponentsWithin(componentScopeId, selector);
|
|
3192
3196
|
componentInstance.selectOwnerComponent = () => ownerScopeId ? context.session.selectOwnerComponent(componentScopeId) : null;
|
|
@@ -3201,7 +3205,7 @@ function renderNodeTree(node, scope, context, ownerJsonPath, ownerFilePath, inst
|
|
|
3201
3205
|
alias: clonedNode.name,
|
|
3202
3206
|
classList: String(clonedNode.attribs?.class ?? "").split(CLASS_SPLIT_RE).map((item) => item.trim()).filter(Boolean),
|
|
3203
3207
|
data: createMergedScopeData(scope.data, componentInstance.properties, componentInstance.data),
|
|
3204
|
-
dataset: collectDataset$
|
|
3208
|
+
dataset: collectDataset$3(clonedNode),
|
|
3205
3209
|
eventBindings: collectComponentEventBindings(clonedNode),
|
|
3206
3210
|
getMethod: (methodName) => {
|
|
3207
3211
|
const method = componentInstance?.[methodName];
|
|
@@ -3312,9 +3316,65 @@ function resolveCapabilityValue(source, schema) {
|
|
|
3312
3316
|
function createHeadlessWx(driver) {
|
|
3313
3317
|
const capabilityTree = {
|
|
3314
3318
|
canIUse: true,
|
|
3319
|
+
canvasToTempFilePath: true,
|
|
3320
|
+
chooseImage: { return: {
|
|
3321
|
+
errMsg: true,
|
|
3322
|
+
tempFilePaths: true,
|
|
3323
|
+
tempFiles: true
|
|
3324
|
+
} },
|
|
3325
|
+
chooseMessageFile: { return: {
|
|
3326
|
+
errMsg: true,
|
|
3327
|
+
tempFiles: true
|
|
3328
|
+
} },
|
|
3329
|
+
chooseMedia: { return: {
|
|
3330
|
+
errMsg: true,
|
|
3331
|
+
tempFiles: true,
|
|
3332
|
+
type: true
|
|
3333
|
+
} },
|
|
3334
|
+
chooseVideo: { return: {
|
|
3335
|
+
duration: true,
|
|
3336
|
+
errMsg: true,
|
|
3337
|
+
height: true,
|
|
3338
|
+
size: true,
|
|
3339
|
+
tempFilePath: true,
|
|
3340
|
+
width: true
|
|
3341
|
+
} },
|
|
3342
|
+
compressImage: { return: {
|
|
3343
|
+
errMsg: true,
|
|
3344
|
+
tempFilePath: true
|
|
3345
|
+
} },
|
|
3315
3346
|
clearStorage: true,
|
|
3316
3347
|
clearStorageSync: true,
|
|
3348
|
+
createAnimation: true,
|
|
3349
|
+
createCanvasContext: true,
|
|
3350
|
+
createIntersectionObserver: true,
|
|
3351
|
+
createVideoContext: true,
|
|
3317
3352
|
createSelectorQuery: true,
|
|
3353
|
+
getImageInfo: { return: {
|
|
3354
|
+
errMsg: true,
|
|
3355
|
+
height: true,
|
|
3356
|
+
orientation: true,
|
|
3357
|
+
path: true,
|
|
3358
|
+
type: true,
|
|
3359
|
+
width: true
|
|
3360
|
+
} },
|
|
3361
|
+
getFileInfo: { return: {
|
|
3362
|
+
digest: true,
|
|
3363
|
+
errMsg: true,
|
|
3364
|
+
size: true
|
|
3365
|
+
} },
|
|
3366
|
+
openDocument: { return: { errMsg: true } },
|
|
3367
|
+
getVideoInfo: { return: {
|
|
3368
|
+
bitrate: true,
|
|
3369
|
+
duration: true,
|
|
3370
|
+
errMsg: true,
|
|
3371
|
+
fps: true,
|
|
3372
|
+
height: true,
|
|
3373
|
+
orientation: true,
|
|
3374
|
+
size: true,
|
|
3375
|
+
type: true,
|
|
3376
|
+
width: true
|
|
3377
|
+
} },
|
|
3318
3378
|
getFileSystemManager: true,
|
|
3319
3379
|
getSavedFileInfo: true,
|
|
3320
3380
|
getSavedFileList: true,
|
|
@@ -3352,6 +3412,10 @@ function createHeadlessWx(driver) {
|
|
|
3352
3412
|
},
|
|
3353
3413
|
scene: true
|
|
3354
3414
|
} },
|
|
3415
|
+
getClipboardData: { return: {
|
|
3416
|
+
data: true,
|
|
3417
|
+
errMsg: true
|
|
3418
|
+
} },
|
|
3355
3419
|
getMenuButtonBoundingClientRect: { return: {
|
|
3356
3420
|
bottom: true,
|
|
3357
3421
|
height: true,
|
|
@@ -3426,14 +3490,18 @@ function createHeadlessWx(driver) {
|
|
|
3426
3490
|
offNetworkStatusChange: true,
|
|
3427
3491
|
onNetworkStatusChange: true,
|
|
3428
3492
|
pageScrollTo: true,
|
|
3493
|
+
previewImage: true,
|
|
3429
3494
|
reLaunch: true,
|
|
3430
3495
|
redirectTo: true,
|
|
3496
|
+
saveImageToPhotosAlbum: true,
|
|
3497
|
+
saveVideoToPhotosAlbum: true,
|
|
3431
3498
|
removeStorage: true,
|
|
3432
3499
|
removeStorageSync: true,
|
|
3433
3500
|
request: true,
|
|
3434
3501
|
saveFile: true,
|
|
3435
3502
|
setBackgroundColor: true,
|
|
3436
3503
|
setBackgroundTextStyle: true,
|
|
3504
|
+
setClipboardData: true,
|
|
3437
3505
|
setStorage: true,
|
|
3438
3506
|
setStorageSync: true,
|
|
3439
3507
|
setNavigationBarColor: true,
|
|
@@ -3450,6 +3518,7 @@ function createHeadlessWx(driver) {
|
|
|
3450
3518
|
showLoading: true,
|
|
3451
3519
|
showModal: true,
|
|
3452
3520
|
showToast: true,
|
|
3521
|
+
startPullDownRefresh: true,
|
|
3453
3522
|
stopPullDownRefresh: true,
|
|
3454
3523
|
switchTab: true,
|
|
3455
3524
|
uploadFile: true,
|
|
@@ -3459,11 +3528,21 @@ function createHeadlessWx(driver) {
|
|
|
3459
3528
|
};
|
|
3460
3529
|
return {
|
|
3461
3530
|
canIUse: (schema) => typeof schema === "string" && schema.trim() !== "" && resolveCapabilityValue(capabilityTree, schema.trim()) != null,
|
|
3531
|
+
canvasToTempFilePath: (option) => invokeWxApi(() => driver.canvasToTempFilePath(option), option),
|
|
3532
|
+
chooseImage: (option) => invokeWxApi(() => driver.chooseImage(option ?? {}), option),
|
|
3533
|
+
chooseMessageFile: (option) => invokeWxApi(() => driver.chooseMessageFile(option ?? {}), option),
|
|
3534
|
+
chooseMedia: (option) => invokeWxApi(() => driver.chooseMedia(option ?? {}), option),
|
|
3535
|
+
chooseVideo: (option) => invokeWxApi(() => driver.chooseVideo(option ?? {}), option),
|
|
3536
|
+
compressImage: (option) => invokeWxApi(() => driver.compressImage(option), option),
|
|
3462
3537
|
clearStorage: (option) => invokeWxApi(() => {
|
|
3463
3538
|
driver.clearStorageSync();
|
|
3464
3539
|
return { errMsg: "clearStorage:ok" };
|
|
3465
3540
|
}, option),
|
|
3466
3541
|
clearStorageSync: () => driver.clearStorageSync(),
|
|
3542
|
+
createAnimation: (option) => driver.createAnimation(option),
|
|
3543
|
+
createCanvasContext: (canvasId, component) => driver.createCanvasContext(canvasId, component),
|
|
3544
|
+
createIntersectionObserver: (component, options) => driver.createIntersectionObserver(component, options),
|
|
3545
|
+
createVideoContext: (videoId, component) => driver.createVideoContext(videoId, component),
|
|
3467
3546
|
createSelectorQuery: () => {
|
|
3468
3547
|
const requests = [];
|
|
3469
3548
|
let scope;
|
|
@@ -3522,12 +3601,16 @@ function createHeadlessWx(driver) {
|
|
|
3522
3601
|
return query;
|
|
3523
3602
|
},
|
|
3524
3603
|
getEnterOptionsSync: () => driver.getEnterOptionsSync(),
|
|
3604
|
+
getFileInfo: (option) => invokeWxApi(() => driver.getFileInfo(option), option),
|
|
3605
|
+
getImageInfo: (option) => invokeWxApi(() => driver.getImageInfo(option), option),
|
|
3606
|
+
getVideoInfo: (option) => invokeWxApi(() => driver.getVideoInfo(option), option),
|
|
3525
3607
|
getFileSystemManager: () => driver.getFileSystemManager(),
|
|
3526
3608
|
getSavedFileInfo: (option) => invokeWxApi(() => driver.getSavedFileInfo(option), option),
|
|
3527
3609
|
getSavedFileList: (option) => invokeWxApi(() => driver.getSavedFileList(option), option),
|
|
3528
3610
|
getAppBaseInfo: (option) => invokeWxApi(() => driver.getAppBaseInfoSync(), option),
|
|
3529
3611
|
getAppBaseInfoSync: () => driver.getAppBaseInfoSync(),
|
|
3530
3612
|
getLaunchOptionsSync: () => driver.getLaunchOptionsSync(),
|
|
3613
|
+
getClipboardData: (option) => invokeWxApi(() => driver.getClipboardData(), option),
|
|
3531
3614
|
getMenuButtonBoundingClientRect: () => driver.getMenuButtonBoundingClientRect(),
|
|
3532
3615
|
getNetworkType: (option) => invokeWxApi(() => driver.getNetworkType(), option),
|
|
3533
3616
|
getStorageInfo: (option) => invokeWxApi(() => driver.getStorageInfoSync(), option),
|
|
@@ -3553,9 +3636,11 @@ function createHeadlessWx(driver) {
|
|
|
3553
3636
|
nextTick: (callback) => driver.nextTick(callback),
|
|
3554
3637
|
offNetworkStatusChange: (callback) => driver.offNetworkStatusChange(callback),
|
|
3555
3638
|
onNetworkStatusChange: (callback) => driver.onNetworkStatusChange(callback),
|
|
3639
|
+
openDocument: (option) => invokeWxApi(() => driver.openDocument(option), option),
|
|
3556
3640
|
pageScrollTo: (option) => invokeWxApi(() => {
|
|
3557
3641
|
driver.pageScrollTo(option);
|
|
3558
3642
|
}, option),
|
|
3643
|
+
previewImage: (option) => invokeWxApi(() => driver.previewImage(option), option),
|
|
3559
3644
|
reLaunch: (option) => invokeWxApi(() => {
|
|
3560
3645
|
driver.reLaunch(option);
|
|
3561
3646
|
}, option),
|
|
@@ -3563,6 +3648,8 @@ function createHeadlessWx(driver) {
|
|
|
3563
3648
|
driver.redirectTo(option);
|
|
3564
3649
|
}, option),
|
|
3565
3650
|
removeSavedFile: (option) => invokeWxApi(() => driver.removeSavedFile(option), option),
|
|
3651
|
+
saveImageToPhotosAlbum: (option) => invokeWxApi(() => driver.saveImageToPhotosAlbum(option), option),
|
|
3652
|
+
saveVideoToPhotosAlbum: (option) => invokeWxApi(() => driver.saveVideoToPhotosAlbum(option), option),
|
|
3566
3653
|
removeStorage: (option) => invokeWxApi(() => {
|
|
3567
3654
|
driver.removeStorageSync(option.key);
|
|
3568
3655
|
return { errMsg: "removeStorage:ok" };
|
|
@@ -3572,6 +3659,7 @@ function createHeadlessWx(driver) {
|
|
|
3572
3659
|
saveFile: (option) => invokeWxApi(() => driver.saveFile(option), option),
|
|
3573
3660
|
setBackgroundColor: (option) => invokeWxApi(() => driver.setBackgroundColor(option), option),
|
|
3574
3661
|
setBackgroundTextStyle: (option) => invokeWxApi(() => driver.setBackgroundTextStyle(option), option),
|
|
3662
|
+
setClipboardData: (option) => invokeWxApi(() => driver.setClipboardData(option), option),
|
|
3575
3663
|
setStorage: (option) => invokeWxApi(() => {
|
|
3576
3664
|
driver.setStorageSync(option.key, option.data);
|
|
3577
3665
|
return { errMsg: "setStorage:ok" };
|
|
@@ -3591,6 +3679,7 @@ function createHeadlessWx(driver) {
|
|
|
3591
3679
|
showLoading: (option) => invokeWxApi(() => driver.showLoading(option), option),
|
|
3592
3680
|
showModal: (option) => invokeWxApi(() => driver.showModal(option), option),
|
|
3593
3681
|
showToast: (option) => invokeWxApi(() => driver.showToast(option), option),
|
|
3682
|
+
startPullDownRefresh: (option) => invokeWxApi(() => driver.startPullDownRefresh(), option),
|
|
3594
3683
|
stopPullDownRefresh: () => driver.stopPullDownRefresh(),
|
|
3595
3684
|
switchTab: (option) => invokeWxApi(() => {
|
|
3596
3685
|
driver.switchTab(option);
|
|
@@ -3802,10 +3891,11 @@ function loadProject(projectPath) {
|
|
|
3802
3891
|
}
|
|
3803
3892
|
//#endregion
|
|
3804
3893
|
//#region ../../mpcore/packages/simulator/src/view/selectors.ts
|
|
3805
|
-
const WHITESPACE_RE = /\s+/;
|
|
3894
|
+
const WHITESPACE_RE$1 = /\s+/;
|
|
3806
3895
|
const DATA_ATTR_SELECTOR_RE$1 = /^\[data-([^=\]]+)="([^"]*)"\]$/;
|
|
3896
|
+
const COMPOUND_SELECTOR_PART_RE$1 = /#[\w-]+|\.[\w-]+|\[data-[^=\]]+="[^"]*"\]|[A-Za-z][\w-]*/g;
|
|
3807
3897
|
function getClassList(node) {
|
|
3808
|
-
return String(node.attribs?.class ?? "").split(WHITESPACE_RE).map((item) => item.trim()).filter(Boolean);
|
|
3898
|
+
return String(node.attribs?.class ?? "").split(WHITESPACE_RE$1).map((item) => item.trim()).filter(Boolean);
|
|
3809
3899
|
}
|
|
3810
3900
|
function matchesSimpleSelector(node, selector) {
|
|
3811
3901
|
if (node.type !== "tag") return false;
|
|
@@ -3819,6 +3909,15 @@ function matchesSimpleSelector(node, selector) {
|
|
|
3819
3909
|
}
|
|
3820
3910
|
return node.name === selector;
|
|
3821
3911
|
}
|
|
3912
|
+
function parseCompoundSelector$1(selector) {
|
|
3913
|
+
const parts = selector.match(COMPOUND_SELECTOR_PART_RE$1) ?? [];
|
|
3914
|
+
return parts.join("") === selector ? parts : [];
|
|
3915
|
+
}
|
|
3916
|
+
function matchesSelectorToken(node, selector) {
|
|
3917
|
+
const simpleSelectors = parseCompoundSelector$1(selector);
|
|
3918
|
+
if (simpleSelectors.length === 0) return false;
|
|
3919
|
+
return simpleSelectors.every((simpleSelector) => matchesSimpleSelector(node, simpleSelector));
|
|
3920
|
+
}
|
|
3822
3921
|
function collectDescendants(node, into) {
|
|
3823
3922
|
for (const child of node.children ?? []) {
|
|
3824
3923
|
into.push(child);
|
|
@@ -3826,7 +3925,7 @@ function collectDescendants(node, into) {
|
|
|
3826
3925
|
}
|
|
3827
3926
|
}
|
|
3828
3927
|
function querySelectorAll(root, selector) {
|
|
3829
|
-
const parts = selector.trim().split(WHITESPACE_RE).filter(Boolean);
|
|
3928
|
+
const parts = selector.trim().split(WHITESPACE_RE$1).filter(Boolean);
|
|
3830
3929
|
if (parts.length === 0) return [];
|
|
3831
3930
|
let current = [root];
|
|
3832
3931
|
for (const part of parts) {
|
|
@@ -3835,7 +3934,7 @@ function querySelectorAll(root, selector) {
|
|
|
3835
3934
|
const candidates = [];
|
|
3836
3935
|
if (part === "page" && node.type === "tag" && node.name === "page") candidates.push(node);
|
|
3837
3936
|
collectDescendants(node, candidates);
|
|
3838
|
-
for (const candidate of candidates) if (
|
|
3937
|
+
for (const candidate of candidates) if (matchesSelectorToken(candidate, part)) next.push(candidate);
|
|
3839
3938
|
}
|
|
3840
3939
|
current = next;
|
|
3841
3940
|
}
|
|
@@ -3843,22 +3942,22 @@ function querySelectorAll(root, selector) {
|
|
|
3843
3942
|
}
|
|
3844
3943
|
//#endregion
|
|
3845
3944
|
//#region ../../mpcore/packages/simulator/src/view/nodeHandle.ts
|
|
3846
|
-
const DATASET_NAME_RE$
|
|
3945
|
+
const DATASET_NAME_RE$2 = /-([a-z])/g;
|
|
3847
3946
|
function escapeText$1(text) {
|
|
3848
3947
|
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
3849
3948
|
}
|
|
3850
|
-
function toDatasetKey$
|
|
3851
|
-
return attributeName.slice(5).replace(DATASET_NAME_RE$
|
|
3949
|
+
function toDatasetKey$2(attributeName) {
|
|
3950
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE$2, (_match, char) => char.toUpperCase());
|
|
3852
3951
|
}
|
|
3853
|
-
function collectDataset$
|
|
3952
|
+
function collectDataset$2(node) {
|
|
3854
3953
|
const dataset = {};
|
|
3855
3954
|
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
3856
3955
|
if (!key.startsWith("data-") || key.startsWith("data-sim-")) continue;
|
|
3857
|
-
dataset[toDatasetKey$
|
|
3956
|
+
dataset[toDatasetKey$2(key)] = value;
|
|
3858
3957
|
}
|
|
3859
3958
|
return dataset;
|
|
3860
3959
|
}
|
|
3861
|
-
function resolveEventBinding(node, eventName) {
|
|
3960
|
+
function resolveEventBinding$1(node, eventName) {
|
|
3862
3961
|
const normalizedEventName = eventName.trim();
|
|
3863
3962
|
if (!normalizedEventName) return null;
|
|
3864
3963
|
const bindingAttrs = [
|
|
@@ -3873,8 +3972,8 @@ function resolveEventBinding(node, eventName) {
|
|
|
3873
3972
|
}
|
|
3874
3973
|
return null;
|
|
3875
3974
|
}
|
|
3876
|
-
function createEventPayload(node, eventName, event) {
|
|
3877
|
-
const dataset = collectDataset$
|
|
3975
|
+
function createEventPayload$1(node, eventName, event) {
|
|
3976
|
+
const dataset = collectDataset$2(node);
|
|
3878
3977
|
const nodeId = node.attribs?.id ?? "";
|
|
3879
3978
|
return {
|
|
3880
3979
|
bubbles: false,
|
|
@@ -3939,7 +4038,7 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
|
|
|
3939
4038
|
return this.node.attribs?.[name];
|
|
3940
4039
|
}
|
|
3941
4040
|
async dataset() {
|
|
3942
|
-
return collectDataset$
|
|
4041
|
+
return collectDataset$2(this.node);
|
|
3943
4042
|
}
|
|
3944
4043
|
async scope() {
|
|
3945
4044
|
if (!this.interactions) throw new Error("Node interactions are not available for this headless testing node.");
|
|
@@ -3969,9 +4068,9 @@ var HeadlessTestingNodeHandle = class HeadlessTestingNodeHandle {
|
|
|
3969
4068
|
if (!this.interactions) throw new Error("Node interactions are not available for this headless testing node.");
|
|
3970
4069
|
const normalizedEventName = eventName.trim();
|
|
3971
4070
|
if (!normalizedEventName) throw new Error("Event name must be a non-empty string in headless testing runtime.");
|
|
3972
|
-
const methodName = resolveEventBinding(this.node, normalizedEventName);
|
|
4071
|
+
const methodName = resolveEventBinding$1(this.node, normalizedEventName);
|
|
3973
4072
|
if (!methodName) throw new Error(`No ${normalizedEventName} binding was found on <${this.node.name ?? "unknown"}> in headless testing runtime.`);
|
|
3974
|
-
return await this.interactions.callMethod(this.node.attribs?.["data-sim-scope"] ?? null, methodName, createEventPayload(this.node, normalizedEventName, event));
|
|
4073
|
+
return await this.interactions.callMethod(this.node.attribs?.["data-sim-scope"] ?? null, methodName, createEventPayload$1(this.node, normalizedEventName, event));
|
|
3975
4074
|
}
|
|
3976
4075
|
async tap(event = {}) {
|
|
3977
4076
|
return await this.trigger("tap", event);
|
|
@@ -4055,18 +4154,18 @@ function renderPageTree(project, page) {
|
|
|
4055
4154
|
}
|
|
4056
4155
|
//#endregion
|
|
4057
4156
|
//#region ../../mpcore/packages/simulator/src/view/selectorQuery.ts
|
|
4058
|
-
const DATASET_NAME_RE = /-([a-z])/g;
|
|
4157
|
+
const DATASET_NAME_RE$1 = /-([a-z])/g;
|
|
4059
4158
|
const LEADING_MARK_PREFIX_RE = /^mark[:\-]?/;
|
|
4060
4159
|
const MARK_NAME_RE = /[:\-]([a-z])/g;
|
|
4061
|
-
const NUMERIC_LIKE_VALUE_RE = /-?\d+(?:\.\d+)?/;
|
|
4062
|
-
function toDatasetKey(attributeName) {
|
|
4063
|
-
return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
|
|
4160
|
+
const NUMERIC_LIKE_VALUE_RE$1 = /-?\d+(?:\.\d+)?/;
|
|
4161
|
+
function toDatasetKey$1(attributeName) {
|
|
4162
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE$1, (_match, char) => char.toUpperCase());
|
|
4064
4163
|
}
|
|
4065
|
-
function collectDataset(node) {
|
|
4164
|
+
function collectDataset$1(node) {
|
|
4066
4165
|
const dataset = {};
|
|
4067
4166
|
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
4068
4167
|
if (!key.startsWith("data-") || key.startsWith("data-sim-")) continue;
|
|
4069
|
-
dataset[toDatasetKey(key)] = value;
|
|
4168
|
+
dataset[toDatasetKey$1(key)] = value;
|
|
4070
4169
|
}
|
|
4071
4170
|
return dataset;
|
|
4072
4171
|
}
|
|
@@ -4095,7 +4194,7 @@ function findNodeByScopeId(root, scopeId) {
|
|
|
4095
4194
|
}
|
|
4096
4195
|
return null;
|
|
4097
4196
|
}
|
|
4098
|
-
function parseStyleDeclarations(styleValue) {
|
|
4197
|
+
function parseStyleDeclarations$1(styleValue) {
|
|
4099
4198
|
const declarations = {};
|
|
4100
4199
|
if (!styleValue) return declarations;
|
|
4101
4200
|
for (const declaration of styleValue.split(";")) {
|
|
@@ -4106,17 +4205,17 @@ function parseStyleDeclarations(styleValue) {
|
|
|
4106
4205
|
}
|
|
4107
4206
|
return declarations;
|
|
4108
4207
|
}
|
|
4109
|
-
function parseNumericLikeValue(value) {
|
|
4208
|
+
function parseNumericLikeValue$1(value) {
|
|
4110
4209
|
if (!value) return 0;
|
|
4111
|
-
const match = value.match(NUMERIC_LIKE_VALUE_RE);
|
|
4210
|
+
const match = value.match(NUMERIC_LIKE_VALUE_RE$1);
|
|
4112
4211
|
return match ? Number(match[0]) : 0;
|
|
4113
4212
|
}
|
|
4114
|
-
function resolveRect(node) {
|
|
4115
|
-
const style = parseStyleDeclarations(node.attribs?.style);
|
|
4116
|
-
const left = parseNumericLikeValue(node.attribs?.["data-sim-left"] ?? style.left);
|
|
4117
|
-
const top = parseNumericLikeValue(node.attribs?.["data-sim-top"] ?? style.top);
|
|
4118
|
-
const width = parseNumericLikeValue(node.attribs?.["data-sim-width"] ?? style.width);
|
|
4119
|
-
const height = parseNumericLikeValue(node.attribs?.["data-sim-height"] ?? style.height);
|
|
4213
|
+
function resolveRect$1(node) {
|
|
4214
|
+
const style = parseStyleDeclarations$1(node.attribs?.style);
|
|
4215
|
+
const left = parseNumericLikeValue$1(node.attribs?.["data-sim-left"] ?? style.left);
|
|
4216
|
+
const top = parseNumericLikeValue$1(node.attribs?.["data-sim-top"] ?? style.top);
|
|
4217
|
+
const width = parseNumericLikeValue$1(node.attribs?.["data-sim-width"] ?? style.width);
|
|
4218
|
+
const height = parseNumericLikeValue$1(node.attribs?.["data-sim-height"] ?? style.height);
|
|
4120
4219
|
return {
|
|
4121
4220
|
bottom: top + height,
|
|
4122
4221
|
height,
|
|
@@ -4126,10 +4225,16 @@ function resolveRect(node) {
|
|
|
4126
4225
|
width
|
|
4127
4226
|
};
|
|
4128
4227
|
}
|
|
4228
|
+
function resolveSelectorScrollTop(root, selector) {
|
|
4229
|
+
const normalizedSelector = selector?.trim();
|
|
4230
|
+
if (!normalizedSelector) return null;
|
|
4231
|
+
const match = querySelectorAll(root, normalizedSelector)[0];
|
|
4232
|
+
return match ? resolveRect$1(match).top : null;
|
|
4233
|
+
}
|
|
4129
4234
|
function resolveScrollOffset(node) {
|
|
4130
4235
|
return {
|
|
4131
|
-
scrollLeft: parseNumericLikeValue(node.attribs?.["data-sim-scroll-left"]),
|
|
4132
|
-
scrollTop: parseNumericLikeValue(node.attribs?.["data-sim-scroll-top"])
|
|
4236
|
+
scrollLeft: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-left"]),
|
|
4237
|
+
scrollTop: parseNumericLikeValue$1(node.attribs?.["data-sim-scroll-top"])
|
|
4133
4238
|
};
|
|
4134
4239
|
}
|
|
4135
4240
|
function resolvePropertyValue(node, propertyName) {
|
|
@@ -4137,7 +4242,7 @@ function resolvePropertyValue(node, propertyName) {
|
|
|
4137
4242
|
if (!normalizedPropertyName) return;
|
|
4138
4243
|
if (normalizedPropertyName === "id") return node.attribs?.id ?? "";
|
|
4139
4244
|
if (normalizedPropertyName === "class") return node.attribs?.class ?? "";
|
|
4140
|
-
if (normalizedPropertyName === "dataset") return collectDataset(node);
|
|
4245
|
+
if (normalizedPropertyName === "dataset") return collectDataset$1(node);
|
|
4141
4246
|
return node.attribs?.[normalizedPropertyName];
|
|
4142
4247
|
}
|
|
4143
4248
|
function pickProperties(node, propertyNames) {
|
|
@@ -4146,18 +4251,18 @@ function pickProperties(node, propertyNames) {
|
|
|
4146
4251
|
return result;
|
|
4147
4252
|
}
|
|
4148
4253
|
function pickComputedStyle(node, propertyNames) {
|
|
4149
|
-
const style = parseStyleDeclarations(node.attribs?.style);
|
|
4254
|
+
const style = parseStyleDeclarations$1(node.attribs?.style);
|
|
4150
4255
|
const result = {};
|
|
4151
4256
|
for (const propertyName of propertyNames) result[propertyName] = style[propertyName] ?? "";
|
|
4152
4257
|
return result;
|
|
4153
4258
|
}
|
|
4154
|
-
function resolveFieldsResult(node, fields,
|
|
4259
|
+
function resolveFieldsResult(node, fields, options) {
|
|
4155
4260
|
const result = {};
|
|
4156
4261
|
if (fields.id) result.id = node.attribs?.id ?? "";
|
|
4157
|
-
if (fields.dataset) result.dataset = collectDataset(node);
|
|
4262
|
+
if (fields.dataset) result.dataset = collectDataset$1(node);
|
|
4158
4263
|
if (fields.mark) result.mark = collectMark(node);
|
|
4159
4264
|
if (fields.rect || fields.size) {
|
|
4160
|
-
const rect = resolveRect(node);
|
|
4265
|
+
const rect = resolveRect$1(node);
|
|
4161
4266
|
if (fields.rect) Object.assign(result, rect);
|
|
4162
4267
|
if (fields.size) {
|
|
4163
4268
|
result.width = rect.width;
|
|
@@ -4167,7 +4272,7 @@ function resolveFieldsResult(node, fields, _options) {
|
|
|
4167
4272
|
if (fields.scrollOffset) Object.assign(result, resolveScrollOffset(node));
|
|
4168
4273
|
if (Array.isArray(fields.properties) && fields.properties.length > 0) Object.assign(result, pickProperties(node, fields.properties));
|
|
4169
4274
|
if (Array.isArray(fields.computedStyle) && fields.computedStyle.length > 0) Object.assign(result, pickComputedStyle(node, fields.computedStyle));
|
|
4170
|
-
if (fields.context) result.context = { type: "unsupported-context" };
|
|
4275
|
+
if (fields.context) result.context = options.resolveContext?.(node) ?? { type: "unsupported-context" };
|
|
4171
4276
|
if (fields.node) result.node = { type: node.name ?? "unknown" };
|
|
4172
4277
|
return result;
|
|
4173
4278
|
}
|
|
@@ -4210,6 +4315,749 @@ function resolveSelectorQueryScopeRoot(root, scopeId) {
|
|
|
4210
4315
|
return scopedNode ? createScopedRoot(scopedNode) : root;
|
|
4211
4316
|
}
|
|
4212
4317
|
//#endregion
|
|
4318
|
+
//#region ../../mpcore/packages/simulator/src/view/animation.ts
|
|
4319
|
+
const DEFAULT_STEP_OPTION = {
|
|
4320
|
+
delay: 0,
|
|
4321
|
+
duration: 400,
|
|
4322
|
+
timingFunction: "linear",
|
|
4323
|
+
transformOrigin: "50% 50% 0"
|
|
4324
|
+
};
|
|
4325
|
+
function normalizeStepOption(option) {
|
|
4326
|
+
return {
|
|
4327
|
+
delay: option?.delay ?? DEFAULT_STEP_OPTION.delay,
|
|
4328
|
+
duration: option?.duration ?? DEFAULT_STEP_OPTION.duration,
|
|
4329
|
+
timingFunction: option?.timingFunction ?? DEFAULT_STEP_OPTION.timingFunction,
|
|
4330
|
+
transformOrigin: option?.transformOrigin ?? DEFAULT_STEP_OPTION.transformOrigin
|
|
4331
|
+
};
|
|
4332
|
+
}
|
|
4333
|
+
function normalizeLength(value) {
|
|
4334
|
+
if (typeof value === "number") return `${value}px`;
|
|
4335
|
+
return value ?? "0px";
|
|
4336
|
+
}
|
|
4337
|
+
function createAction(type, args) {
|
|
4338
|
+
return {
|
|
4339
|
+
args,
|
|
4340
|
+
type
|
|
4341
|
+
};
|
|
4342
|
+
}
|
|
4343
|
+
function createHeadlessAnimation(defaultOption) {
|
|
4344
|
+
const baseOption = normalizeStepOption(defaultOption);
|
|
4345
|
+
let currentActions = [];
|
|
4346
|
+
let queue = [];
|
|
4347
|
+
let animation;
|
|
4348
|
+
const append = (type, args) => {
|
|
4349
|
+
currentActions.push(createAction(type, args));
|
|
4350
|
+
return animation;
|
|
4351
|
+
};
|
|
4352
|
+
animation = {
|
|
4353
|
+
backgroundColor(value) {
|
|
4354
|
+
return append("backgroundColor", [value]);
|
|
4355
|
+
},
|
|
4356
|
+
bottom(value) {
|
|
4357
|
+
return append("bottom", [normalizeLength(value)]);
|
|
4358
|
+
},
|
|
4359
|
+
export() {
|
|
4360
|
+
const exported = queue.map((item) => ({
|
|
4361
|
+
animates: item.animates.map((action) => ({
|
|
4362
|
+
args: [...action.args],
|
|
4363
|
+
type: action.type
|
|
4364
|
+
})),
|
|
4365
|
+
option: { ...item.option }
|
|
4366
|
+
}));
|
|
4367
|
+
queue = [];
|
|
4368
|
+
currentActions = [];
|
|
4369
|
+
return { actions: exported };
|
|
4370
|
+
},
|
|
4371
|
+
height(value) {
|
|
4372
|
+
return append("height", [normalizeLength(value)]);
|
|
4373
|
+
},
|
|
4374
|
+
left(value) {
|
|
4375
|
+
return append("left", [normalizeLength(value)]);
|
|
4376
|
+
},
|
|
4377
|
+
opacity(value) {
|
|
4378
|
+
return append("opacity", [value]);
|
|
4379
|
+
},
|
|
4380
|
+
right(value) {
|
|
4381
|
+
return append("right", [normalizeLength(value)]);
|
|
4382
|
+
},
|
|
4383
|
+
rotate(angle) {
|
|
4384
|
+
return append("rotate", [`${angle}deg`]);
|
|
4385
|
+
},
|
|
4386
|
+
scale(sx, sy) {
|
|
4387
|
+
return append("scale", [sx, sy ?? sx]);
|
|
4388
|
+
},
|
|
4389
|
+
step(option) {
|
|
4390
|
+
queue.push({
|
|
4391
|
+
animates: currentActions.map((action) => ({
|
|
4392
|
+
args: [...action.args],
|
|
4393
|
+
type: action.type
|
|
4394
|
+
})),
|
|
4395
|
+
option: {
|
|
4396
|
+
...baseOption,
|
|
4397
|
+
...option ?? {}
|
|
4398
|
+
}
|
|
4399
|
+
});
|
|
4400
|
+
currentActions = [];
|
|
4401
|
+
return animation;
|
|
4402
|
+
},
|
|
4403
|
+
top(value) {
|
|
4404
|
+
return append("top", [normalizeLength(value)]);
|
|
4405
|
+
},
|
|
4406
|
+
translate(tx, ty) {
|
|
4407
|
+
return append("translate", [normalizeLength(tx), normalizeLength(ty)]);
|
|
4408
|
+
},
|
|
4409
|
+
translate3d(tx, ty, tz) {
|
|
4410
|
+
return append("translate3d", [
|
|
4411
|
+
normalizeLength(tx),
|
|
4412
|
+
normalizeLength(ty),
|
|
4413
|
+
normalizeLength(tz)
|
|
4414
|
+
]);
|
|
4415
|
+
},
|
|
4416
|
+
translateX(translation) {
|
|
4417
|
+
return append("translateX", [normalizeLength(translation)]);
|
|
4418
|
+
},
|
|
4419
|
+
translateY(translation) {
|
|
4420
|
+
return append("translateY", [normalizeLength(translation)]);
|
|
4421
|
+
},
|
|
4422
|
+
translateZ(translation) {
|
|
4423
|
+
return append("translateZ", [normalizeLength(translation)]);
|
|
4424
|
+
},
|
|
4425
|
+
width(value) {
|
|
4426
|
+
return append("width", [normalizeLength(value)]);
|
|
4427
|
+
}
|
|
4428
|
+
};
|
|
4429
|
+
return animation;
|
|
4430
|
+
}
|
|
4431
|
+
//#endregion
|
|
4432
|
+
//#region ../../mpcore/packages/simulator/src/view/canvasContext.ts
|
|
4433
|
+
function findCanvasNode(root, canvasId) {
|
|
4434
|
+
if (root.type === "tag" && root.name === "canvas" && root.attribs?.["canvas-id"] === canvasId) return root;
|
|
4435
|
+
for (const child of root.children ?? []) {
|
|
4436
|
+
const match = findCanvasNode(child, canvasId);
|
|
4437
|
+
if (match) return match;
|
|
4438
|
+
}
|
|
4439
|
+
return null;
|
|
4440
|
+
}
|
|
4441
|
+
function cloneCall(call) {
|
|
4442
|
+
return {
|
|
4443
|
+
args: call.args.map((arg) => Array.isArray(arg) ? [...arg] : arg),
|
|
4444
|
+
type: call.type
|
|
4445
|
+
};
|
|
4446
|
+
}
|
|
4447
|
+
function createHeadlessCanvasContext(driver, canvasId, scope) {
|
|
4448
|
+
const defaultState = {
|
|
4449
|
+
fillStyle: "#000000",
|
|
4450
|
+
fontSize: 16,
|
|
4451
|
+
globalAlpha: 1,
|
|
4452
|
+
lineCap: "butt",
|
|
4453
|
+
lineDash: [],
|
|
4454
|
+
lineDashOffset: 0,
|
|
4455
|
+
lineJoin: "miter",
|
|
4456
|
+
miterLimit: 10,
|
|
4457
|
+
lineWidth: 1,
|
|
4458
|
+
shadowBlur: 0,
|
|
4459
|
+
shadowColor: "#000000",
|
|
4460
|
+
shadowOffsetX: 0,
|
|
4461
|
+
shadowOffsetY: 0,
|
|
4462
|
+
strokeStyle: "#000000",
|
|
4463
|
+
textAlign: "start",
|
|
4464
|
+
textBaseline: "alphabetic"
|
|
4465
|
+
};
|
|
4466
|
+
let state = { ...defaultState };
|
|
4467
|
+
let stateStack = [];
|
|
4468
|
+
let drawCalls = [];
|
|
4469
|
+
let snapshot = {
|
|
4470
|
+
canvasId,
|
|
4471
|
+
drawCalls: [],
|
|
4472
|
+
fillStyle: state.fillStyle,
|
|
4473
|
+
fontSize: state.fontSize,
|
|
4474
|
+
globalAlpha: state.globalAlpha,
|
|
4475
|
+
lineCap: state.lineCap,
|
|
4476
|
+
lineDash: [...state.lineDash],
|
|
4477
|
+
lineDashOffset: state.lineDashOffset,
|
|
4478
|
+
lineJoin: state.lineJoin,
|
|
4479
|
+
miterLimit: state.miterLimit,
|
|
4480
|
+
lineWidth: state.lineWidth,
|
|
4481
|
+
reserve: false,
|
|
4482
|
+
shadowBlur: state.shadowBlur,
|
|
4483
|
+
shadowColor: state.shadowColor,
|
|
4484
|
+
shadowOffsetX: state.shadowOffsetX,
|
|
4485
|
+
shadowOffsetY: state.shadowOffsetY,
|
|
4486
|
+
strokeStyle: state.strokeStyle,
|
|
4487
|
+
textAlign: state.textAlign,
|
|
4488
|
+
textBaseline: state.textBaseline
|
|
4489
|
+
};
|
|
4490
|
+
const resolveCanvas = () => {
|
|
4491
|
+
const scopeResolution = driver.resolveScope(scope);
|
|
4492
|
+
if (scopeResolution.kind === "missing") return null;
|
|
4493
|
+
const rendered = driver.renderCurrentPage();
|
|
4494
|
+
const root = scopeResolution.kind === "component" ? resolveSelectorQueryScopeRoot(rendered.root, scopeResolution.scopeId) : rendered.root;
|
|
4495
|
+
return root ? findCanvasNode(root, canvasId) : null;
|
|
4496
|
+
};
|
|
4497
|
+
const ensureCanvas = () => {
|
|
4498
|
+
const node = resolveCanvas();
|
|
4499
|
+
if (!node) throw new Error(`Canvas with canvas-id "${canvasId}" was not found in headless runtime.`);
|
|
4500
|
+
return node;
|
|
4501
|
+
};
|
|
4502
|
+
const record = (type, args) => {
|
|
4503
|
+
ensureCanvas();
|
|
4504
|
+
drawCalls.push({
|
|
4505
|
+
args,
|
|
4506
|
+
type
|
|
4507
|
+
});
|
|
4508
|
+
};
|
|
4509
|
+
return {
|
|
4510
|
+
arc(x, y, r, sAngle, eAngle, counterclockwise) {
|
|
4511
|
+
record("arc", [
|
|
4512
|
+
x,
|
|
4513
|
+
y,
|
|
4514
|
+
r,
|
|
4515
|
+
sAngle,
|
|
4516
|
+
eAngle,
|
|
4517
|
+
Boolean(counterclockwise)
|
|
4518
|
+
]);
|
|
4519
|
+
},
|
|
4520
|
+
arcTo(x1, y1, x2, y2, radius) {
|
|
4521
|
+
record("arcTo", [
|
|
4522
|
+
x1,
|
|
4523
|
+
y1,
|
|
4524
|
+
x2,
|
|
4525
|
+
y2,
|
|
4526
|
+
radius
|
|
4527
|
+
]);
|
|
4528
|
+
},
|
|
4529
|
+
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
|
|
4530
|
+
record("bezierCurveTo", [
|
|
4531
|
+
cp1x,
|
|
4532
|
+
cp1y,
|
|
4533
|
+
cp2x,
|
|
4534
|
+
cp2y,
|
|
4535
|
+
x,
|
|
4536
|
+
y
|
|
4537
|
+
]);
|
|
4538
|
+
},
|
|
4539
|
+
__getSnapshot() {
|
|
4540
|
+
return {
|
|
4541
|
+
canvasId: snapshot.canvasId,
|
|
4542
|
+
drawCalls: snapshot.drawCalls.map(cloneCall),
|
|
4543
|
+
fillStyle: snapshot.fillStyle,
|
|
4544
|
+
fontSize: snapshot.fontSize,
|
|
4545
|
+
globalAlpha: snapshot.globalAlpha,
|
|
4546
|
+
lineCap: snapshot.lineCap,
|
|
4547
|
+
lineDash: [...snapshot.lineDash],
|
|
4548
|
+
lineDashOffset: snapshot.lineDashOffset,
|
|
4549
|
+
lineJoin: snapshot.lineJoin,
|
|
4550
|
+
miterLimit: snapshot.miterLimit,
|
|
4551
|
+
lineWidth: snapshot.lineWidth,
|
|
4552
|
+
reserve: snapshot.reserve,
|
|
4553
|
+
shadowBlur: snapshot.shadowBlur,
|
|
4554
|
+
shadowColor: snapshot.shadowColor,
|
|
4555
|
+
shadowOffsetX: snapshot.shadowOffsetX,
|
|
4556
|
+
shadowOffsetY: snapshot.shadowOffsetY,
|
|
4557
|
+
strokeStyle: snapshot.strokeStyle,
|
|
4558
|
+
textAlign: snapshot.textAlign,
|
|
4559
|
+
textBaseline: snapshot.textBaseline
|
|
4560
|
+
};
|
|
4561
|
+
},
|
|
4562
|
+
beginPath() {
|
|
4563
|
+
record("beginPath", []);
|
|
4564
|
+
},
|
|
4565
|
+
clearRect(x, y, width, height) {
|
|
4566
|
+
record("clearRect", [
|
|
4567
|
+
x,
|
|
4568
|
+
y,
|
|
4569
|
+
width,
|
|
4570
|
+
height
|
|
4571
|
+
]);
|
|
4572
|
+
},
|
|
4573
|
+
clip(fillRule) {
|
|
4574
|
+
record("clip", fillRule == null ? [] : [fillRule]);
|
|
4575
|
+
},
|
|
4576
|
+
closePath() {
|
|
4577
|
+
record("closePath", []);
|
|
4578
|
+
},
|
|
4579
|
+
draw(reserve, callback) {
|
|
4580
|
+
ensureCanvas();
|
|
4581
|
+
snapshot = {
|
|
4582
|
+
canvasId,
|
|
4583
|
+
drawCalls: reserve ? [...snapshot.drawCalls.map(cloneCall), ...drawCalls.map(cloneCall)] : drawCalls.map(cloneCall),
|
|
4584
|
+
fillStyle: state.fillStyle,
|
|
4585
|
+
fontSize: state.fontSize,
|
|
4586
|
+
globalAlpha: state.globalAlpha,
|
|
4587
|
+
lineCap: state.lineCap,
|
|
4588
|
+
lineDash: [...state.lineDash],
|
|
4589
|
+
lineDashOffset: state.lineDashOffset,
|
|
4590
|
+
lineJoin: state.lineJoin,
|
|
4591
|
+
miterLimit: state.miterLimit,
|
|
4592
|
+
lineWidth: state.lineWidth,
|
|
4593
|
+
reserve: Boolean(reserve),
|
|
4594
|
+
shadowBlur: state.shadowBlur,
|
|
4595
|
+
shadowColor: state.shadowColor,
|
|
4596
|
+
shadowOffsetX: state.shadowOffsetX,
|
|
4597
|
+
shadowOffsetY: state.shadowOffsetY,
|
|
4598
|
+
strokeStyle: state.strokeStyle,
|
|
4599
|
+
textAlign: state.textAlign,
|
|
4600
|
+
textBaseline: state.textBaseline
|
|
4601
|
+
};
|
|
4602
|
+
drawCalls = [];
|
|
4603
|
+
state = { ...defaultState };
|
|
4604
|
+
stateStack = [];
|
|
4605
|
+
callback?.();
|
|
4606
|
+
},
|
|
4607
|
+
drawImage(image, ...args) {
|
|
4608
|
+
record("drawImage", [image, ...args]);
|
|
4609
|
+
},
|
|
4610
|
+
fill(fillRule) {
|
|
4611
|
+
record("fill", fillRule == null ? [] : [fillRule]);
|
|
4612
|
+
},
|
|
4613
|
+
fillRect(x, y, width, height) {
|
|
4614
|
+
record("fillRect", [
|
|
4615
|
+
x,
|
|
4616
|
+
y,
|
|
4617
|
+
width,
|
|
4618
|
+
height
|
|
4619
|
+
]);
|
|
4620
|
+
},
|
|
4621
|
+
fillText(text, x, y, maxWidth) {
|
|
4622
|
+
record("fillText", maxWidth == null ? [
|
|
4623
|
+
text,
|
|
4624
|
+
x,
|
|
4625
|
+
y
|
|
4626
|
+
] : [
|
|
4627
|
+
text,
|
|
4628
|
+
x,
|
|
4629
|
+
y,
|
|
4630
|
+
maxWidth
|
|
4631
|
+
]);
|
|
4632
|
+
},
|
|
4633
|
+
lineTo(x, y) {
|
|
4634
|
+
record("lineTo", [x, y]);
|
|
4635
|
+
},
|
|
4636
|
+
measureText(text) {
|
|
4637
|
+
return { width: text.length * state.fontSize * .5 };
|
|
4638
|
+
},
|
|
4639
|
+
moveTo(x, y) {
|
|
4640
|
+
record("moveTo", [x, y]);
|
|
4641
|
+
},
|
|
4642
|
+
quadraticCurveTo(cpx, cpy, x, y) {
|
|
4643
|
+
record("quadraticCurveTo", [
|
|
4644
|
+
cpx,
|
|
4645
|
+
cpy,
|
|
4646
|
+
x,
|
|
4647
|
+
y
|
|
4648
|
+
]);
|
|
4649
|
+
},
|
|
4650
|
+
rect(x, y, width, height) {
|
|
4651
|
+
record("rect", [
|
|
4652
|
+
x,
|
|
4653
|
+
y,
|
|
4654
|
+
width,
|
|
4655
|
+
height
|
|
4656
|
+
]);
|
|
4657
|
+
},
|
|
4658
|
+
restore() {
|
|
4659
|
+
state = stateStack.pop() ?? { ...defaultState };
|
|
4660
|
+
record("restore", []);
|
|
4661
|
+
},
|
|
4662
|
+
rotate(rotate) {
|
|
4663
|
+
record("rotate", [rotate]);
|
|
4664
|
+
},
|
|
4665
|
+
save() {
|
|
4666
|
+
stateStack.push({ ...state });
|
|
4667
|
+
record("save", []);
|
|
4668
|
+
},
|
|
4669
|
+
scale(scaleWidth, scaleHeight) {
|
|
4670
|
+
record("scale", [scaleWidth, scaleHeight]);
|
|
4671
|
+
},
|
|
4672
|
+
setFillStyle(value) {
|
|
4673
|
+
state.fillStyle = String(value);
|
|
4674
|
+
record("setFillStyle", [value]);
|
|
4675
|
+
},
|
|
4676
|
+
setFontSize(fontSize) {
|
|
4677
|
+
state.fontSize = Number(fontSize);
|
|
4678
|
+
record("setFontSize", [fontSize]);
|
|
4679
|
+
},
|
|
4680
|
+
setGlobalAlpha(value) {
|
|
4681
|
+
state.globalAlpha = Number(value);
|
|
4682
|
+
record("setGlobalAlpha", [value]);
|
|
4683
|
+
},
|
|
4684
|
+
setLineCap(value) {
|
|
4685
|
+
state.lineCap = String(value);
|
|
4686
|
+
record("setLineCap", [value]);
|
|
4687
|
+
},
|
|
4688
|
+
setLineDash(pattern, offset) {
|
|
4689
|
+
state.lineDash = pattern.map((item) => Number(item));
|
|
4690
|
+
state.lineDashOffset = offset == null ? 0 : Number(offset);
|
|
4691
|
+
record("setLineDash", offset == null ? [pattern.map((item) => Number(item))] : [pattern.map((item) => Number(item)), Number(offset)]);
|
|
4692
|
+
},
|
|
4693
|
+
setLineJoin(value) {
|
|
4694
|
+
state.lineJoin = String(value);
|
|
4695
|
+
record("setLineJoin", [value]);
|
|
4696
|
+
},
|
|
4697
|
+
setMiterLimit(value) {
|
|
4698
|
+
state.miterLimit = Number(value);
|
|
4699
|
+
record("setMiterLimit", [value]);
|
|
4700
|
+
},
|
|
4701
|
+
setLineWidth(value) {
|
|
4702
|
+
state.lineWidth = Number(value);
|
|
4703
|
+
record("setLineWidth", [value]);
|
|
4704
|
+
},
|
|
4705
|
+
setShadow(offsetX, offsetY, blur, color) {
|
|
4706
|
+
state.shadowOffsetX = Number(offsetX);
|
|
4707
|
+
state.shadowOffsetY = Number(offsetY);
|
|
4708
|
+
state.shadowBlur = Number(blur);
|
|
4709
|
+
state.shadowColor = String(color);
|
|
4710
|
+
record("setShadow", [
|
|
4711
|
+
offsetX,
|
|
4712
|
+
offsetY,
|
|
4713
|
+
blur,
|
|
4714
|
+
color
|
|
4715
|
+
]);
|
|
4716
|
+
},
|
|
4717
|
+
setStrokeStyle(value) {
|
|
4718
|
+
state.strokeStyle = String(value);
|
|
4719
|
+
record("setStrokeStyle", [value]);
|
|
4720
|
+
},
|
|
4721
|
+
setTextAlign(value) {
|
|
4722
|
+
state.textAlign = String(value);
|
|
4723
|
+
record("setTextAlign", [value]);
|
|
4724
|
+
},
|
|
4725
|
+
setTextBaseline(value) {
|
|
4726
|
+
state.textBaseline = String(value);
|
|
4727
|
+
record("setTextBaseline", [value]);
|
|
4728
|
+
},
|
|
4729
|
+
stroke() {
|
|
4730
|
+
record("stroke", []);
|
|
4731
|
+
},
|
|
4732
|
+
strokeRect(x, y, width, height) {
|
|
4733
|
+
record("strokeRect", [
|
|
4734
|
+
x,
|
|
4735
|
+
y,
|
|
4736
|
+
width,
|
|
4737
|
+
height
|
|
4738
|
+
]);
|
|
4739
|
+
},
|
|
4740
|
+
strokeText(text, x, y, maxWidth) {
|
|
4741
|
+
record("strokeText", maxWidth == null ? [
|
|
4742
|
+
text,
|
|
4743
|
+
x,
|
|
4744
|
+
y
|
|
4745
|
+
] : [
|
|
4746
|
+
text,
|
|
4747
|
+
x,
|
|
4748
|
+
y,
|
|
4749
|
+
maxWidth
|
|
4750
|
+
]);
|
|
4751
|
+
},
|
|
4752
|
+
translate(x, y) {
|
|
4753
|
+
record("translate", [x, y]);
|
|
4754
|
+
}
|
|
4755
|
+
};
|
|
4756
|
+
}
|
|
4757
|
+
//#endregion
|
|
4758
|
+
//#region ../../mpcore/packages/simulator/src/view/intersectionObserver.ts
|
|
4759
|
+
const NUMERIC_LIKE_VALUE_RE = /-?\d+(?:\.\d+)?/;
|
|
4760
|
+
function normalizeMargins(margins) {
|
|
4761
|
+
return {
|
|
4762
|
+
bottom: Number(margins?.bottom) || 0,
|
|
4763
|
+
left: Number(margins?.left) || 0,
|
|
4764
|
+
right: Number(margins?.right) || 0,
|
|
4765
|
+
top: Number(margins?.top) || 0
|
|
4766
|
+
};
|
|
4767
|
+
}
|
|
4768
|
+
function parseStyleDeclarations(styleValue) {
|
|
4769
|
+
const declarations = {};
|
|
4770
|
+
if (!styleValue) return declarations;
|
|
4771
|
+
for (const declaration of styleValue.split(";")) {
|
|
4772
|
+
const [rawProperty, ...rawValueParts] = declaration.split(":");
|
|
4773
|
+
const property = rawProperty?.trim();
|
|
4774
|
+
if (!property) continue;
|
|
4775
|
+
declarations[property] = rawValueParts.join(":").trim();
|
|
4776
|
+
}
|
|
4777
|
+
return declarations;
|
|
4778
|
+
}
|
|
4779
|
+
function parseNumericLikeValue(value) {
|
|
4780
|
+
if (!value) return 0;
|
|
4781
|
+
const match = value.match(NUMERIC_LIKE_VALUE_RE);
|
|
4782
|
+
return match ? Number(match[0]) : 0;
|
|
4783
|
+
}
|
|
4784
|
+
function resolveRect(node) {
|
|
4785
|
+
const style = parseStyleDeclarations(node.attribs?.style);
|
|
4786
|
+
const left = parseNumericLikeValue(node.attribs?.["data-sim-left"] ?? style.left);
|
|
4787
|
+
const top = parseNumericLikeValue(node.attribs?.["data-sim-top"] ?? style.top);
|
|
4788
|
+
const width = parseNumericLikeValue(node.attribs?.["data-sim-width"] ?? style.width);
|
|
4789
|
+
const height = parseNumericLikeValue(node.attribs?.["data-sim-height"] ?? style.height);
|
|
4790
|
+
return {
|
|
4791
|
+
bottom: top + height,
|
|
4792
|
+
height,
|
|
4793
|
+
left,
|
|
4794
|
+
right: left + width,
|
|
4795
|
+
top,
|
|
4796
|
+
width
|
|
4797
|
+
};
|
|
4798
|
+
}
|
|
4799
|
+
function applyMargins(rect, margins) {
|
|
4800
|
+
const top = rect.top - (margins.top ?? 0);
|
|
4801
|
+
const left = rect.left - (margins.left ?? 0);
|
|
4802
|
+
const right = rect.right + (margins.right ?? 0);
|
|
4803
|
+
const bottom = rect.bottom + (margins.bottom ?? 0);
|
|
4804
|
+
return {
|
|
4805
|
+
bottom,
|
|
4806
|
+
height: Math.max(0, bottom - top),
|
|
4807
|
+
left,
|
|
4808
|
+
right,
|
|
4809
|
+
top,
|
|
4810
|
+
width: Math.max(0, right - left)
|
|
4811
|
+
};
|
|
4812
|
+
}
|
|
4813
|
+
function intersectRects(source, target) {
|
|
4814
|
+
const left = Math.max(source.left, target.left);
|
|
4815
|
+
const top = Math.max(source.top, target.top);
|
|
4816
|
+
const right = Math.min(source.right, target.right);
|
|
4817
|
+
const bottom = Math.min(source.bottom, target.bottom);
|
|
4818
|
+
if (right <= left || bottom <= top) return {
|
|
4819
|
+
bottom: top,
|
|
4820
|
+
height: 0,
|
|
4821
|
+
left,
|
|
4822
|
+
right: left,
|
|
4823
|
+
top,
|
|
4824
|
+
width: 0
|
|
4825
|
+
};
|
|
4826
|
+
return {
|
|
4827
|
+
bottom,
|
|
4828
|
+
height: bottom - top,
|
|
4829
|
+
left,
|
|
4830
|
+
right,
|
|
4831
|
+
top,
|
|
4832
|
+
width: right - left
|
|
4833
|
+
};
|
|
4834
|
+
}
|
|
4835
|
+
function resolveViewportRect(windowInfo) {
|
|
4836
|
+
return {
|
|
4837
|
+
bottom: windowInfo.windowHeight,
|
|
4838
|
+
height: windowInfo.windowHeight,
|
|
4839
|
+
left: 0,
|
|
4840
|
+
right: windowInfo.windowWidth,
|
|
4841
|
+
top: 0,
|
|
4842
|
+
width: windowInfo.windowWidth
|
|
4843
|
+
};
|
|
4844
|
+
}
|
|
4845
|
+
function createHeadlessIntersectionObserver(driver, scope, _options) {
|
|
4846
|
+
let active = true;
|
|
4847
|
+
let relativeTarget = {
|
|
4848
|
+
kind: "viewport",
|
|
4849
|
+
margins: normalizeMargins()
|
|
4850
|
+
};
|
|
4851
|
+
const resolveScopedRoot = () => {
|
|
4852
|
+
const scopeResolution = driver.resolveScope(scope);
|
|
4853
|
+
if (scopeResolution.kind === "missing") return null;
|
|
4854
|
+
const rendered = driver.renderCurrentPage();
|
|
4855
|
+
return scopeResolution.kind === "component" ? resolveSelectorQueryScopeRoot(rendered.root, scopeResolution.scopeId) : rendered.root;
|
|
4856
|
+
};
|
|
4857
|
+
const resolveTargetNode = (selector) => {
|
|
4858
|
+
const scopedRoot = resolveScopedRoot();
|
|
4859
|
+
if (!scopedRoot) return null;
|
|
4860
|
+
return querySelectorAll(scopedRoot, selector)[0] ?? null;
|
|
4861
|
+
};
|
|
4862
|
+
const resolveRelativeRect = () => {
|
|
4863
|
+
if (relativeTarget.kind === "viewport") return applyMargins(resolveViewportRect(driver.getWindowInfo()), relativeTarget.margins);
|
|
4864
|
+
const relativeNode = resolveTargetNode(relativeTarget.selector);
|
|
4865
|
+
if (!relativeNode) return null;
|
|
4866
|
+
return applyMargins(resolveRect(relativeNode), relativeTarget.margins);
|
|
4867
|
+
};
|
|
4868
|
+
return {
|
|
4869
|
+
disconnect() {
|
|
4870
|
+
active = false;
|
|
4871
|
+
},
|
|
4872
|
+
observe(selector, callback) {
|
|
4873
|
+
if (!active) return;
|
|
4874
|
+
const targetNode = resolveTargetNode(selector);
|
|
4875
|
+
const relativeRect = resolveRelativeRect();
|
|
4876
|
+
if (!targetNode || !relativeRect) return;
|
|
4877
|
+
const boundingClientRect = resolveRect(targetNode);
|
|
4878
|
+
const intersectionRect = intersectRects(boundingClientRect, relativeRect);
|
|
4879
|
+
const targetArea = boundingClientRect.width * boundingClientRect.height;
|
|
4880
|
+
const intersectionArea = intersectionRect.width * intersectionRect.height;
|
|
4881
|
+
callback({
|
|
4882
|
+
boundingClientRect,
|
|
4883
|
+
id: targetNode.attribs?.id ?? "",
|
|
4884
|
+
intersectionRatio: targetArea > 0 ? intersectionArea / targetArea : 0,
|
|
4885
|
+
intersectionRect,
|
|
4886
|
+
relativeRect
|
|
4887
|
+
});
|
|
4888
|
+
},
|
|
4889
|
+
relativeTo(selector, margins) {
|
|
4890
|
+
relativeTarget = {
|
|
4891
|
+
kind: "selector",
|
|
4892
|
+
margins: normalizeMargins(margins),
|
|
4893
|
+
selector
|
|
4894
|
+
};
|
|
4895
|
+
return this;
|
|
4896
|
+
},
|
|
4897
|
+
relativeToViewport(margins) {
|
|
4898
|
+
relativeTarget = {
|
|
4899
|
+
kind: "viewport",
|
|
4900
|
+
margins: normalizeMargins(margins)
|
|
4901
|
+
};
|
|
4902
|
+
return this;
|
|
4903
|
+
}
|
|
4904
|
+
};
|
|
4905
|
+
}
|
|
4906
|
+
//#endregion
|
|
4907
|
+
//#region ../../mpcore/packages/simulator/src/view/mediaQueryObserver.ts
|
|
4908
|
+
function resolveOrientation(windowInfo) {
|
|
4909
|
+
return windowInfo.windowWidth >= windowInfo.windowHeight ? "landscape" : "portrait";
|
|
4910
|
+
}
|
|
4911
|
+
function resolveMatches(descriptor, windowInfo) {
|
|
4912
|
+
if (descriptor.width != null && windowInfo.windowWidth !== descriptor.width) return false;
|
|
4913
|
+
if (descriptor.minWidth != null && windowInfo.windowWidth < descriptor.minWidth) return false;
|
|
4914
|
+
if (descriptor.maxWidth != null && windowInfo.windowWidth > descriptor.maxWidth) return false;
|
|
4915
|
+
if (descriptor.height != null && windowInfo.windowHeight !== descriptor.height) return false;
|
|
4916
|
+
if (descriptor.minHeight != null && windowInfo.windowHeight < descriptor.minHeight) return false;
|
|
4917
|
+
if (descriptor.maxHeight != null && windowInfo.windowHeight > descriptor.maxHeight) return false;
|
|
4918
|
+
if (descriptor.orientation != null && resolveOrientation(windowInfo) !== descriptor.orientation) return false;
|
|
4919
|
+
return true;
|
|
4920
|
+
}
|
|
4921
|
+
function createHeadlessMediaQueryObserver(driver, onDisconnect) {
|
|
4922
|
+
let active = true;
|
|
4923
|
+
let callback;
|
|
4924
|
+
let descriptor;
|
|
4925
|
+
let hasObserved = false;
|
|
4926
|
+
let lastMatches;
|
|
4927
|
+
const emit = (force) => {
|
|
4928
|
+
if (!active || !hasObserved || !callback || !descriptor) return;
|
|
4929
|
+
const matches = resolveMatches(descriptor, driver.getWindowInfo());
|
|
4930
|
+
if (!force && lastMatches === matches) return;
|
|
4931
|
+
lastMatches = matches;
|
|
4932
|
+
callback({ matches });
|
|
4933
|
+
};
|
|
4934
|
+
const observer = {
|
|
4935
|
+
disconnect() {
|
|
4936
|
+
if (!active) return;
|
|
4937
|
+
active = false;
|
|
4938
|
+
hasObserved = false;
|
|
4939
|
+
callback = void 0;
|
|
4940
|
+
descriptor = void 0;
|
|
4941
|
+
onDisconnect?.();
|
|
4942
|
+
},
|
|
4943
|
+
observe(nextDescriptor, nextCallback) {
|
|
4944
|
+
if (!active) return;
|
|
4945
|
+
descriptor = { ...nextDescriptor };
|
|
4946
|
+
callback = nextCallback;
|
|
4947
|
+
hasObserved = true;
|
|
4948
|
+
emit(true);
|
|
4949
|
+
}
|
|
4950
|
+
};
|
|
4951
|
+
return {
|
|
4952
|
+
disconnect: observer.disconnect,
|
|
4953
|
+
notify() {
|
|
4954
|
+
emit(false);
|
|
4955
|
+
},
|
|
4956
|
+
observer
|
|
4957
|
+
};
|
|
4958
|
+
}
|
|
4959
|
+
//#endregion
|
|
4960
|
+
//#region ../../mpcore/packages/simulator/src/view/videoContext.ts
|
|
4961
|
+
const DATASET_NAME_RE = /-([a-z])/g;
|
|
4962
|
+
function toDatasetKey(attributeName) {
|
|
4963
|
+
return attributeName.slice(5).replace(DATASET_NAME_RE, (_match, char) => char.toUpperCase());
|
|
4964
|
+
}
|
|
4965
|
+
function collectDataset(node) {
|
|
4966
|
+
const dataset = {};
|
|
4967
|
+
for (const [key, value] of Object.entries(node.attribs ?? {})) {
|
|
4968
|
+
if (!key.startsWith("data-") || key.startsWith("data-sim-")) continue;
|
|
4969
|
+
dataset[toDatasetKey(key)] = value;
|
|
4970
|
+
}
|
|
4971
|
+
return dataset;
|
|
4972
|
+
}
|
|
4973
|
+
function resolveEventBinding(node, eventName) {
|
|
4974
|
+
const normalizedEventName = eventName.trim();
|
|
4975
|
+
if (!normalizedEventName) return null;
|
|
4976
|
+
const bindingAttrs = [
|
|
4977
|
+
`bind${normalizedEventName}`,
|
|
4978
|
+
`bind:${normalizedEventName}`,
|
|
4979
|
+
`catch${normalizedEventName}`,
|
|
4980
|
+
`catch:${normalizedEventName}`
|
|
4981
|
+
];
|
|
4982
|
+
for (const attributeName of bindingAttrs) {
|
|
4983
|
+
const methodName = node.attribs?.[attributeName]?.trim();
|
|
4984
|
+
if (methodName) return methodName;
|
|
4985
|
+
}
|
|
4986
|
+
return null;
|
|
4987
|
+
}
|
|
4988
|
+
function createEventPayload(node, eventName, detail) {
|
|
4989
|
+
const dataset = collectDataset(node);
|
|
4990
|
+
const nodeId = node.attribs?.id ?? "";
|
|
4991
|
+
return {
|
|
4992
|
+
bubbles: false,
|
|
4993
|
+
capturePhase: false,
|
|
4994
|
+
composed: false,
|
|
4995
|
+
currentTarget: {
|
|
4996
|
+
dataset,
|
|
4997
|
+
id: nodeId
|
|
4998
|
+
},
|
|
4999
|
+
detail,
|
|
5000
|
+
mark: void 0,
|
|
5001
|
+
target: {
|
|
5002
|
+
dataset,
|
|
5003
|
+
id: nodeId
|
|
5004
|
+
},
|
|
5005
|
+
type: eventName
|
|
5006
|
+
};
|
|
5007
|
+
}
|
|
5008
|
+
function createHeadlessVideoContext(driver, videoId, scope) {
|
|
5009
|
+
const state = {
|
|
5010
|
+
currentTime: 0,
|
|
5011
|
+
fullScreen: false,
|
|
5012
|
+
paused: true
|
|
5013
|
+
};
|
|
5014
|
+
const resolveNode = () => {
|
|
5015
|
+
const scopeResolution = driver.resolveScope(scope);
|
|
5016
|
+
if (scopeResolution.kind === "missing") return null;
|
|
5017
|
+
const rendered = driver.renderCurrentPage();
|
|
5018
|
+
return querySelectorAll(scopeResolution.kind === "component" ? resolveSelectorQueryScopeRoot(rendered.root, scopeResolution.scopeId) : rendered.root, `#${videoId}`)[0] ?? null;
|
|
5019
|
+
};
|
|
5020
|
+
const dispatch = (eventName, detail) => {
|
|
5021
|
+
const node = resolveNode();
|
|
5022
|
+
if (!node) return;
|
|
5023
|
+
const methodName = resolveEventBinding(node, eventName);
|
|
5024
|
+
if (!methodName) return;
|
|
5025
|
+
driver.callScopeMethod(node.attribs?.["data-sim-scope"] ?? null, methodName, createEventPayload(node, eventName, detail));
|
|
5026
|
+
};
|
|
5027
|
+
return {
|
|
5028
|
+
exitFullScreen() {
|
|
5029
|
+
state.fullScreen = false;
|
|
5030
|
+
dispatch("fullscreenchange", {
|
|
5031
|
+
currentTime: state.currentTime,
|
|
5032
|
+
fullScreen: false
|
|
5033
|
+
});
|
|
5034
|
+
},
|
|
5035
|
+
pause() {
|
|
5036
|
+
state.paused = true;
|
|
5037
|
+
dispatch("pause", { currentTime: state.currentTime });
|
|
5038
|
+
},
|
|
5039
|
+
play() {
|
|
5040
|
+
state.paused = false;
|
|
5041
|
+
dispatch("play", { currentTime: state.currentTime });
|
|
5042
|
+
},
|
|
5043
|
+
requestFullScreen() {
|
|
5044
|
+
state.fullScreen = true;
|
|
5045
|
+
dispatch("fullscreenchange", {
|
|
5046
|
+
currentTime: state.currentTime,
|
|
5047
|
+
fullScreen: true
|
|
5048
|
+
});
|
|
5049
|
+
},
|
|
5050
|
+
seek(position) {
|
|
5051
|
+
state.currentTime = Number.isFinite(position) ? Number(position) : 0;
|
|
5052
|
+
},
|
|
5053
|
+
stop() {
|
|
5054
|
+
state.currentTime = 0;
|
|
5055
|
+
state.paused = true;
|
|
5056
|
+
dispatch("pause", { currentTime: state.currentTime });
|
|
5057
|
+
}
|
|
5058
|
+
};
|
|
5059
|
+
}
|
|
5060
|
+
//#endregion
|
|
4213
5061
|
//#region ../../mpcore/packages/simulator/src/runtime/moduleLoader.ts
|
|
4214
5062
|
function createRequireNotFoundError(request, importer) {
|
|
4215
5063
|
return /* @__PURE__ */ new Error(`Cannot resolve require("${request}") from ${normalize(importer)} in headless runtime.`);
|
|
@@ -4387,9 +5235,228 @@ const DEFAULT_MODAL_CONFIRM_TEXT = "确定";
|
|
|
4387
5235
|
const TRAILING_SLASH_RE = /\/+$/;
|
|
4388
5236
|
const SAVED_FILE_PREFIXES = ["headless://saved/", "headless://wxfile/saved/"];
|
|
4389
5237
|
const textEncoder = new TextEncoder();
|
|
5238
|
+
const IMAGE_EXTENSION_RE = /\.([^.?#/]+)(?:[?#].*)?$/;
|
|
5239
|
+
const LEADING_DOT_RE = /^\.+/;
|
|
5240
|
+
const IMAGE_FILE_EXTENSIONS = new Set([
|
|
5241
|
+
"bmp",
|
|
5242
|
+
"gif",
|
|
5243
|
+
"heic",
|
|
5244
|
+
"jpeg",
|
|
5245
|
+
"jpg",
|
|
5246
|
+
"png",
|
|
5247
|
+
"webp"
|
|
5248
|
+
]);
|
|
5249
|
+
const VIDEO_FILE_EXTENSIONS = new Set([
|
|
5250
|
+
"avi",
|
|
5251
|
+
"m4v",
|
|
5252
|
+
"mov",
|
|
5253
|
+
"mp4",
|
|
5254
|
+
"mpeg",
|
|
5255
|
+
"mpg",
|
|
5256
|
+
"webm"
|
|
5257
|
+
]);
|
|
4390
5258
|
function byteLength(input) {
|
|
4391
5259
|
return textEncoder.encode(input).byteLength;
|
|
4392
5260
|
}
|
|
5261
|
+
function rotateLeft(value, bits) {
|
|
5262
|
+
return (value << bits | value >>> 32 - bits) >>> 0;
|
|
5263
|
+
}
|
|
5264
|
+
function toHexWordLittleEndian(value) {
|
|
5265
|
+
let output = "";
|
|
5266
|
+
for (let index = 0; index < 4; index += 1) output += (value >>> index * 8 & 255).toString(16).padStart(2, "0");
|
|
5267
|
+
return output;
|
|
5268
|
+
}
|
|
5269
|
+
function toHexWordBigEndian(value) {
|
|
5270
|
+
return value.toString(16).padStart(8, "0");
|
|
5271
|
+
}
|
|
5272
|
+
function computeMd5Digest(input) {
|
|
5273
|
+
const bytes = Array.from(textEncoder.encode(input));
|
|
5274
|
+
const bitLength = bytes.length * 8;
|
|
5275
|
+
const lowBits = bitLength >>> 0;
|
|
5276
|
+
const highBits = Math.floor(bitLength / 4294967296) >>> 0;
|
|
5277
|
+
bytes.push(128);
|
|
5278
|
+
while (bytes.length % 64 !== 56) bytes.push(0);
|
|
5279
|
+
for (let index = 0; index < 4; index += 1) bytes.push(lowBits >>> index * 8 & 255);
|
|
5280
|
+
for (let index = 0; index < 4; index += 1) bytes.push(highBits >>> index * 8 & 255);
|
|
5281
|
+
const shifts = [
|
|
5282
|
+
7,
|
|
5283
|
+
12,
|
|
5284
|
+
17,
|
|
5285
|
+
22,
|
|
5286
|
+
7,
|
|
5287
|
+
12,
|
|
5288
|
+
17,
|
|
5289
|
+
22,
|
|
5290
|
+
7,
|
|
5291
|
+
12,
|
|
5292
|
+
17,
|
|
5293
|
+
22,
|
|
5294
|
+
7,
|
|
5295
|
+
12,
|
|
5296
|
+
17,
|
|
5297
|
+
22,
|
|
5298
|
+
5,
|
|
5299
|
+
9,
|
|
5300
|
+
14,
|
|
5301
|
+
20,
|
|
5302
|
+
5,
|
|
5303
|
+
9,
|
|
5304
|
+
14,
|
|
5305
|
+
20,
|
|
5306
|
+
5,
|
|
5307
|
+
9,
|
|
5308
|
+
14,
|
|
5309
|
+
20,
|
|
5310
|
+
5,
|
|
5311
|
+
9,
|
|
5312
|
+
14,
|
|
5313
|
+
20,
|
|
5314
|
+
4,
|
|
5315
|
+
11,
|
|
5316
|
+
16,
|
|
5317
|
+
23,
|
|
5318
|
+
4,
|
|
5319
|
+
11,
|
|
5320
|
+
16,
|
|
5321
|
+
23,
|
|
5322
|
+
4,
|
|
5323
|
+
11,
|
|
5324
|
+
16,
|
|
5325
|
+
23,
|
|
5326
|
+
4,
|
|
5327
|
+
11,
|
|
5328
|
+
16,
|
|
5329
|
+
23,
|
|
5330
|
+
6,
|
|
5331
|
+
10,
|
|
5332
|
+
15,
|
|
5333
|
+
21,
|
|
5334
|
+
6,
|
|
5335
|
+
10,
|
|
5336
|
+
15,
|
|
5337
|
+
21,
|
|
5338
|
+
6,
|
|
5339
|
+
10,
|
|
5340
|
+
15,
|
|
5341
|
+
21,
|
|
5342
|
+
6,
|
|
5343
|
+
10,
|
|
5344
|
+
15,
|
|
5345
|
+
21
|
|
5346
|
+
];
|
|
5347
|
+
const table = Array.from({ length: 64 }, (_, index) => Math.floor(Math.abs(Math.sin(index + 1)) * 4294967296) >>> 0);
|
|
5348
|
+
let a0 = 1732584193;
|
|
5349
|
+
let b0 = 4023233417;
|
|
5350
|
+
let c0 = 2562383102;
|
|
5351
|
+
let d0 = 271733878;
|
|
5352
|
+
for (let chunkOffset = 0; chunkOffset < bytes.length; chunkOffset += 64) {
|
|
5353
|
+
const words = Array.from({ length: 16 }, (_, index) => {
|
|
5354
|
+
const wordOffset = chunkOffset + index * 4;
|
|
5355
|
+
return (bytes[wordOffset] | bytes[wordOffset + 1] << 8 | bytes[wordOffset + 2] << 16 | bytes[wordOffset + 3] << 24) >>> 0;
|
|
5356
|
+
});
|
|
5357
|
+
let a = a0;
|
|
5358
|
+
let b = b0;
|
|
5359
|
+
let c = c0;
|
|
5360
|
+
let d = d0;
|
|
5361
|
+
for (let index = 0; index < 64; index += 1) {
|
|
5362
|
+
let f = 0;
|
|
5363
|
+
let g = 0;
|
|
5364
|
+
if (index < 16) {
|
|
5365
|
+
f = (b & c | ~b & d) >>> 0;
|
|
5366
|
+
g = index;
|
|
5367
|
+
} else if (index < 32) {
|
|
5368
|
+
f = (d & b | ~d & c) >>> 0;
|
|
5369
|
+
g = (5 * index + 1) % 16;
|
|
5370
|
+
} else if (index < 48) {
|
|
5371
|
+
f = (b ^ c ^ d) >>> 0;
|
|
5372
|
+
g = (3 * index + 5) % 16;
|
|
5373
|
+
} else {
|
|
5374
|
+
f = (c ^ (b | ~d)) >>> 0;
|
|
5375
|
+
g = 7 * index % 16;
|
|
5376
|
+
}
|
|
5377
|
+
const next = a + f + table[index] + words[g] >>> 0;
|
|
5378
|
+
a = d;
|
|
5379
|
+
d = c;
|
|
5380
|
+
c = b;
|
|
5381
|
+
b = b + rotateLeft(next, shifts[index]) >>> 0;
|
|
5382
|
+
}
|
|
5383
|
+
a0 = a0 + a >>> 0;
|
|
5384
|
+
b0 = b0 + b >>> 0;
|
|
5385
|
+
c0 = c0 + c >>> 0;
|
|
5386
|
+
d0 = d0 + d >>> 0;
|
|
5387
|
+
}
|
|
5388
|
+
return [
|
|
5389
|
+
toHexWordLittleEndian(a0),
|
|
5390
|
+
toHexWordLittleEndian(b0),
|
|
5391
|
+
toHexWordLittleEndian(c0),
|
|
5392
|
+
toHexWordLittleEndian(d0)
|
|
5393
|
+
].join("");
|
|
5394
|
+
}
|
|
5395
|
+
function computeSha1Digest(input) {
|
|
5396
|
+
const bytes = Array.from(textEncoder.encode(input));
|
|
5397
|
+
const bitLength = bytes.length * 8;
|
|
5398
|
+
const lowBits = bitLength >>> 0;
|
|
5399
|
+
const highBits = Math.floor(bitLength / 4294967296) >>> 0;
|
|
5400
|
+
bytes.push(128);
|
|
5401
|
+
while (bytes.length % 64 !== 56) bytes.push(0);
|
|
5402
|
+
for (let index = 3; index >= 0; index -= 1) bytes.push(highBits >>> index * 8 & 255);
|
|
5403
|
+
for (let index = 3; index >= 0; index -= 1) bytes.push(lowBits >>> index * 8 & 255);
|
|
5404
|
+
let h0 = 1732584193;
|
|
5405
|
+
let h1 = 4023233417;
|
|
5406
|
+
let h2 = 2562383102;
|
|
5407
|
+
let h3 = 271733878;
|
|
5408
|
+
let h4 = 3285377520;
|
|
5409
|
+
for (let chunkOffset = 0; chunkOffset < bytes.length; chunkOffset += 64) {
|
|
5410
|
+
const words = Array.from({ length: 80 }, (_, index) => {
|
|
5411
|
+
if (index < 16) {
|
|
5412
|
+
const wordOffset = chunkOffset + index * 4;
|
|
5413
|
+
return (bytes[wordOffset] << 24 | bytes[wordOffset + 1] << 16 | bytes[wordOffset + 2] << 8 | bytes[wordOffset + 3]) >>> 0;
|
|
5414
|
+
}
|
|
5415
|
+
return 0;
|
|
5416
|
+
});
|
|
5417
|
+
for (let index = 16; index < 80; index += 1) words[index] = rotateLeft(words[index - 3] ^ words[index - 8] ^ words[index - 14] ^ words[index - 16], 1);
|
|
5418
|
+
let a = h0;
|
|
5419
|
+
let b = h1;
|
|
5420
|
+
let c = h2;
|
|
5421
|
+
let d = h3;
|
|
5422
|
+
let e = h4;
|
|
5423
|
+
for (let index = 0; index < 80; index += 1) {
|
|
5424
|
+
let f = 0;
|
|
5425
|
+
let k = 0;
|
|
5426
|
+
if (index < 20) {
|
|
5427
|
+
f = (b & c | ~b & d) >>> 0;
|
|
5428
|
+
k = 1518500249;
|
|
5429
|
+
} else if (index < 40) {
|
|
5430
|
+
f = (b ^ c ^ d) >>> 0;
|
|
5431
|
+
k = 1859775393;
|
|
5432
|
+
} else if (index < 60) {
|
|
5433
|
+
f = (b & c | b & d | c & d) >>> 0;
|
|
5434
|
+
k = 2400959708;
|
|
5435
|
+
} else {
|
|
5436
|
+
f = (b ^ c ^ d) >>> 0;
|
|
5437
|
+
k = 3395469782;
|
|
5438
|
+
}
|
|
5439
|
+
const next = rotateLeft(a, 5) + f + e + k + words[index] >>> 0;
|
|
5440
|
+
e = d;
|
|
5441
|
+
d = c;
|
|
5442
|
+
c = rotateLeft(b, 30);
|
|
5443
|
+
b = a;
|
|
5444
|
+
a = next;
|
|
5445
|
+
}
|
|
5446
|
+
h0 = h0 + a >>> 0;
|
|
5447
|
+
h1 = h1 + b >>> 0;
|
|
5448
|
+
h2 = h2 + c >>> 0;
|
|
5449
|
+
h3 = h3 + d >>> 0;
|
|
5450
|
+
h4 = h4 + e >>> 0;
|
|
5451
|
+
}
|
|
5452
|
+
return [
|
|
5453
|
+
toHexWordBigEndian(h0),
|
|
5454
|
+
toHexWordBigEndian(h1),
|
|
5455
|
+
toHexWordBigEndian(h2),
|
|
5456
|
+
toHexWordBigEndian(h3),
|
|
5457
|
+
toHexWordBigEndian(h4)
|
|
5458
|
+
].join("");
|
|
5459
|
+
}
|
|
4393
5460
|
function cloneShareMenuSnapshot(snapshot) {
|
|
4394
5461
|
return {
|
|
4395
5462
|
isUpdatableMessage: snapshot.isUpdatableMessage,
|
|
@@ -4398,6 +5465,116 @@ function cloneShareMenuSnapshot(snapshot) {
|
|
|
4398
5465
|
withShareTicket: snapshot.withShareTicket
|
|
4399
5466
|
};
|
|
4400
5467
|
}
|
|
5468
|
+
function normalizeImageType(type) {
|
|
5469
|
+
const normalized = (type ?? "").trim().toLowerCase();
|
|
5470
|
+
if (normalized === "jpg") return "jpeg";
|
|
5471
|
+
return normalized;
|
|
5472
|
+
}
|
|
5473
|
+
function normalizeFileExtension(extension) {
|
|
5474
|
+
return extension.trim().replace(LEADING_DOT_RE, "").toLowerCase();
|
|
5475
|
+
}
|
|
5476
|
+
function inferDocumentFileType(filePath) {
|
|
5477
|
+
const extensionMatch = filePath.match(IMAGE_EXTENSION_RE);
|
|
5478
|
+
return extensionMatch ? normalizeFileExtension(extensionMatch[1]) : "unknown";
|
|
5479
|
+
}
|
|
5480
|
+
function isImageFileExtension(extension) {
|
|
5481
|
+
return IMAGE_FILE_EXTENSIONS.has(normalizeFileExtension(extension));
|
|
5482
|
+
}
|
|
5483
|
+
function isVideoFileExtension(extension) {
|
|
5484
|
+
return VIDEO_FILE_EXTENSIONS.has(normalizeFileExtension(extension));
|
|
5485
|
+
}
|
|
5486
|
+
function resolveMessageFileExtensions(option) {
|
|
5487
|
+
const filteredExtensions = (Array.isArray(option.extension) ? option.extension.filter((item) => typeof item === "string").map(normalizeFileExtension).filter(Boolean) : []).filter((extension) => {
|
|
5488
|
+
if (option.type === "image") return isImageFileExtension(extension);
|
|
5489
|
+
if (option.type === "video") return isVideoFileExtension(extension);
|
|
5490
|
+
if (option.type === "file") return !isImageFileExtension(extension) && !isVideoFileExtension(extension);
|
|
5491
|
+
return true;
|
|
5492
|
+
});
|
|
5493
|
+
if (filteredExtensions.length > 0) return filteredExtensions;
|
|
5494
|
+
if (option.type === "image") return ["png", "jpg"];
|
|
5495
|
+
if (option.type === "video") return ["mp4"];
|
|
5496
|
+
if (option.type === "file") return [
|
|
5497
|
+
"txt",
|
|
5498
|
+
"pdf",
|
|
5499
|
+
"doc"
|
|
5500
|
+
];
|
|
5501
|
+
return [
|
|
5502
|
+
"png",
|
|
5503
|
+
"pdf",
|
|
5504
|
+
"mp4"
|
|
5505
|
+
];
|
|
5506
|
+
}
|
|
5507
|
+
function inferImageType(filePath, fileContent) {
|
|
5508
|
+
const extensionMatch = filePath.match(IMAGE_EXTENSION_RE);
|
|
5509
|
+
if (extensionMatch) return normalizeImageType(extensionMatch[1]);
|
|
5510
|
+
try {
|
|
5511
|
+
return normalizeImageType(JSON.parse(fileContent)?.config?.fileType) || "png";
|
|
5512
|
+
} catch {
|
|
5513
|
+
return "unknown";
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
function inferImageSize(fileContent) {
|
|
5517
|
+
try {
|
|
5518
|
+
const payload = JSON.parse(fileContent);
|
|
5519
|
+
return {
|
|
5520
|
+
height: payload?.config?.destHeight ?? payload?.config?.height ?? 0,
|
|
5521
|
+
width: payload?.config?.destWidth ?? payload?.config?.width ?? 0
|
|
5522
|
+
};
|
|
5523
|
+
} catch {
|
|
5524
|
+
return {
|
|
5525
|
+
height: 0,
|
|
5526
|
+
width: 0
|
|
5527
|
+
};
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5530
|
+
function buildImageTempFilePayload(fileType, width, height, metadata = {}) {
|
|
5531
|
+
return JSON.stringify({ config: {
|
|
5532
|
+
fileType,
|
|
5533
|
+
height,
|
|
5534
|
+
quality: metadata.quality,
|
|
5535
|
+
sizeType: metadata.sizeType ?? [],
|
|
5536
|
+
sourceType: metadata.sourceType ?? [],
|
|
5537
|
+
width
|
|
5538
|
+
} });
|
|
5539
|
+
}
|
|
5540
|
+
function buildVideoTempFilePayload(width, height, duration, metadata = {}) {
|
|
5541
|
+
return JSON.stringify({ config: {
|
|
5542
|
+
bitrate: metadata.bitrate ?? 1500,
|
|
5543
|
+
compressed: metadata.compressed ?? true,
|
|
5544
|
+
duration,
|
|
5545
|
+
fps: metadata.fps ?? 30,
|
|
5546
|
+
height,
|
|
5547
|
+
maxDuration: metadata.maxDuration ?? duration,
|
|
5548
|
+
sourceType: metadata.sourceType ?? [],
|
|
5549
|
+
width
|
|
5550
|
+
} });
|
|
5551
|
+
}
|
|
5552
|
+
function inferVideoInfo(filePath, fileContent) {
|
|
5553
|
+
const size = byteLength(fileContent);
|
|
5554
|
+
const type = filePath.match(IMAGE_EXTENSION_RE)?.[1]?.toLowerCase() ?? "mp4";
|
|
5555
|
+
try {
|
|
5556
|
+
const payload = JSON.parse(fileContent);
|
|
5557
|
+
return {
|
|
5558
|
+
bitrate: payload.config.bitrate ?? 1500,
|
|
5559
|
+
duration: payload.config.duration ?? 0,
|
|
5560
|
+
fps: payload.config.fps ?? 30,
|
|
5561
|
+
height: payload.config.height ?? 0,
|
|
5562
|
+
size,
|
|
5563
|
+
type,
|
|
5564
|
+
width: payload.config.width ?? 0
|
|
5565
|
+
};
|
|
5566
|
+
} catch {
|
|
5567
|
+
return {
|
|
5568
|
+
bitrate: 1500,
|
|
5569
|
+
duration: 0,
|
|
5570
|
+
fps: 30,
|
|
5571
|
+
height: 0,
|
|
5572
|
+
size,
|
|
5573
|
+
type,
|
|
5574
|
+
width: 0
|
|
5575
|
+
};
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
4401
5578
|
function createNetworkSnapshot(networkType) {
|
|
4402
5579
|
return {
|
|
4403
5580
|
isConnected: networkType !== "none",
|
|
@@ -4548,6 +5725,9 @@ function createHeadlessWxState() {
|
|
|
4548
5725
|
let fileId = 0;
|
|
4549
5726
|
let loading = null;
|
|
4550
5727
|
let networkType = "wifi";
|
|
5728
|
+
let previewImage = null;
|
|
5729
|
+
let openedDocument = null;
|
|
5730
|
+
let clipboardData = "";
|
|
4551
5731
|
let shareMenu = {
|
|
4552
5732
|
isUpdatableMessage: false,
|
|
4553
5733
|
menus: [],
|
|
@@ -4749,6 +5929,16 @@ function createHeadlessWxState() {
|
|
|
4749
5929
|
size: savedFile.size
|
|
4750
5930
|
};
|
|
4751
5931
|
};
|
|
5932
|
+
const getFileInfo = (option) => {
|
|
5933
|
+
const normalizedPath = normalizeFsPath(option.filePath);
|
|
5934
|
+
const fileContent = files.get(normalizedPath);
|
|
5935
|
+
if (fileContent == null) throw new Error(`getFileInfo:fail no such file or directory, stat '${normalizedPath}'`);
|
|
5936
|
+
return {
|
|
5937
|
+
digest: (option.digestAlgorithm === "sha1" ? "sha1" : "md5") === "sha1" ? computeSha1Digest(fileContent) : computeMd5Digest(fileContent),
|
|
5938
|
+
errMsg: "getFileInfo:ok",
|
|
5939
|
+
size: byteLength(fileContent)
|
|
5940
|
+
};
|
|
5941
|
+
};
|
|
4752
5942
|
const removeSavedFile = (filePath) => {
|
|
4753
5943
|
const normalizedPath = normalizeFsPath(filePath);
|
|
4754
5944
|
if (!savedFiles.has(normalizedPath)) throw new Error(`removeSavedFile:fail no such file or directory, unlink '${normalizedPath}'`);
|
|
@@ -4999,18 +6189,191 @@ function createHeadlessWxState() {
|
|
|
4999
6189
|
getSavedFileInfo(option) {
|
|
5000
6190
|
return getSavedFileInfo(option.filePath);
|
|
5001
6191
|
},
|
|
6192
|
+
getFileInfo(option) {
|
|
6193
|
+
return getFileInfo(option);
|
|
6194
|
+
},
|
|
5002
6195
|
getSavedFileList() {
|
|
5003
6196
|
return getSavedFileList();
|
|
5004
6197
|
},
|
|
5005
6198
|
getFileText(filePath) {
|
|
5006
6199
|
return files.get(filePath) ?? null;
|
|
5007
6200
|
},
|
|
6201
|
+
createTempFile(fileContent, preferredPath) {
|
|
6202
|
+
const tempFilePath = allocateFilePath("temp", preferredPath);
|
|
6203
|
+
ensureDirectoryTree(tempFilePath);
|
|
6204
|
+
files.set(tempFilePath, String(fileContent));
|
|
6205
|
+
return tempFilePath;
|
|
6206
|
+
},
|
|
6207
|
+
chooseImage(option) {
|
|
6208
|
+
const count = Number.isFinite(option.count) ? Math.max(1, Math.min(9, Math.trunc(option.count ?? 1))) : 9;
|
|
6209
|
+
const sizeType = Array.isArray(option.sizeType) && option.sizeType.length > 0 ? option.sizeType.filter((item) => typeof item === "string") : ["original"];
|
|
6210
|
+
const sourceType = Array.isArray(option.sourceType) && option.sourceType.length > 0 ? option.sourceType.filter((item) => typeof item === "string") : ["album", "camera"];
|
|
6211
|
+
const fileType = sizeType.includes("compressed") ? "jpg" : "png";
|
|
6212
|
+
const tempFiles = Array.from({ length: count }, (_, index) => {
|
|
6213
|
+
const payload = buildImageTempFilePayload(fileType, 160 + index * 12, 120 + index * 8, {
|
|
6214
|
+
sizeType: [...sizeType],
|
|
6215
|
+
sourceType: [...sourceType]
|
|
6216
|
+
});
|
|
6217
|
+
return {
|
|
6218
|
+
path: this.createTempFile(payload, `headless://wxfile/temp/chosen-image-${String(index + 1).padStart(2, "0")}.${fileType}`),
|
|
6219
|
+
size: payload.length
|
|
6220
|
+
};
|
|
6221
|
+
});
|
|
6222
|
+
return {
|
|
6223
|
+
errMsg: "chooseImage:ok",
|
|
6224
|
+
tempFilePaths: tempFiles.map((item) => item.path),
|
|
6225
|
+
tempFiles
|
|
6226
|
+
};
|
|
6227
|
+
},
|
|
6228
|
+
chooseMessageFile(option) {
|
|
6229
|
+
const count = Number.isFinite(option.count) ? Math.max(1, Math.min(100, Math.trunc(option.count ?? 1))) : 1;
|
|
6230
|
+
const extensions = resolveMessageFileExtensions(option);
|
|
6231
|
+
return {
|
|
6232
|
+
errMsg: "chooseMessageFile:ok",
|
|
6233
|
+
tempFiles: Array.from({ length: count }, (_, index) => {
|
|
6234
|
+
const extension = extensions[index % extensions.length];
|
|
6235
|
+
const sequence = String(index + 1).padStart(2, "0");
|
|
6236
|
+
const fileName = `message-file-${sequence}.${extension}`;
|
|
6237
|
+
let fileContent = `headless message file ${fileName}`;
|
|
6238
|
+
if (isImageFileExtension(extension)) fileContent = buildImageTempFilePayload(extension, 160 + index * 12, 120 + index * 8, { sourceType: ["message"] });
|
|
6239
|
+
else if (isVideoFileExtension(extension)) fileContent = buildVideoTempFilePayload(640, 360, 18 + index, {
|
|
6240
|
+
compressed: true,
|
|
6241
|
+
sourceType: ["message"]
|
|
6242
|
+
});
|
|
6243
|
+
return {
|
|
6244
|
+
name: fileName,
|
|
6245
|
+
path: this.createTempFile(fileContent, `headless://wxfile/temp/chosen-message-file-${sequence}.${extension}`),
|
|
6246
|
+
size: byteLength(fileContent),
|
|
6247
|
+
time: 171e10 + index * 1e3,
|
|
6248
|
+
type: extension
|
|
6249
|
+
};
|
|
6250
|
+
})
|
|
6251
|
+
};
|
|
6252
|
+
},
|
|
6253
|
+
chooseMedia(option) {
|
|
6254
|
+
const count = Number.isFinite(option.count) ? Math.max(1, Math.min(9, Math.trunc(option.count ?? 1))) : 1;
|
|
6255
|
+
const mediaTypes = Array.isArray(option.mediaType) && option.mediaType.length > 0 ? option.mediaType.filter((item) => item === "image" || item === "video") : ["image"];
|
|
6256
|
+
const sourceType = Array.isArray(option.sourceType) && option.sourceType.length > 0 ? option.sourceType.filter((item) => typeof item === "string") : ["album", "camera"];
|
|
6257
|
+
const sizeType = Array.isArray(option.sizeType) && option.sizeType.length > 0 ? option.sizeType.filter((item) => typeof item === "string") : ["compressed"];
|
|
6258
|
+
const maxDuration = Number.isFinite(option.maxDuration) ? Math.max(1, Math.trunc(option.maxDuration ?? 60)) : 60;
|
|
6259
|
+
return {
|
|
6260
|
+
errMsg: "chooseMedia:ok",
|
|
6261
|
+
tempFiles: Array.from({ length: count }, (_, index) => {
|
|
6262
|
+
if (mediaTypes[index % mediaTypes.length] === "video") {
|
|
6263
|
+
const duration = Math.min(maxDuration, 18 + index);
|
|
6264
|
+
const width = 640;
|
|
6265
|
+
const height = 360;
|
|
6266
|
+
const payload = buildVideoTempFilePayload(width, height, duration, {
|
|
6267
|
+
compressed: sizeType.includes("compressed"),
|
|
6268
|
+
maxDuration,
|
|
6269
|
+
sourceType: [...sourceType]
|
|
6270
|
+
});
|
|
6271
|
+
const tempFilePath = this.createTempFile(payload, `headless://wxfile/temp/chosen-media-video-${String(index + 1).padStart(2, "0")}.mp4`);
|
|
6272
|
+
const thumbPayload = buildImageTempFilePayload("jpg", 160, 90, {
|
|
6273
|
+
sizeType: ["compressed"],
|
|
6274
|
+
sourceType: [...sourceType]
|
|
6275
|
+
});
|
|
6276
|
+
const thumbTempFilePath = this.createTempFile(thumbPayload, `headless://wxfile/temp/chosen-media-video-thumb-${String(index + 1).padStart(2, "0")}.jpg`);
|
|
6277
|
+
return {
|
|
6278
|
+
duration,
|
|
6279
|
+
fileType: "video",
|
|
6280
|
+
height,
|
|
6281
|
+
size: payload.length,
|
|
6282
|
+
tempFilePath,
|
|
6283
|
+
thumbTempFilePath,
|
|
6284
|
+
width
|
|
6285
|
+
};
|
|
6286
|
+
}
|
|
6287
|
+
const width = 160 + index * 12;
|
|
6288
|
+
const height = 120 + index * 8;
|
|
6289
|
+
const fileType = sizeType.includes("compressed") ? "jpg" : "png";
|
|
6290
|
+
const payload = buildImageTempFilePayload(fileType, width, height, {
|
|
6291
|
+
sizeType: [...sizeType],
|
|
6292
|
+
sourceType: [...sourceType]
|
|
6293
|
+
});
|
|
6294
|
+
return {
|
|
6295
|
+
fileType: "image",
|
|
6296
|
+
height,
|
|
6297
|
+
size: payload.length,
|
|
6298
|
+
tempFilePath: this.createTempFile(payload, `headless://wxfile/temp/chosen-media-image-${String(index + 1).padStart(2, "0")}.${fileType}`),
|
|
6299
|
+
width
|
|
6300
|
+
};
|
|
6301
|
+
}),
|
|
6302
|
+
type: mediaTypes.length > 1 ? "mix" : mediaTypes[0]
|
|
6303
|
+
};
|
|
6304
|
+
},
|
|
6305
|
+
chooseVideo(option) {
|
|
6306
|
+
const compressed = option.compressed !== false;
|
|
6307
|
+
const maxDuration = Number.isFinite(option.maxDuration) ? Math.max(1, Math.trunc(option.maxDuration ?? 60)) : 60;
|
|
6308
|
+
const sourceType = Array.isArray(option.sourceType) && option.sourceType.length > 0 ? option.sourceType.filter((item) => typeof item === "string") : ["album", "camera"];
|
|
6309
|
+
const payload = JSON.stringify({ config: {
|
|
6310
|
+
compressed,
|
|
6311
|
+
duration: Math.min(maxDuration, compressed ? 18 : 36),
|
|
6312
|
+
height: compressed ? 360 : 720,
|
|
6313
|
+
maxDuration,
|
|
6314
|
+
sourceType: [...sourceType],
|
|
6315
|
+
width: compressed ? 640 : 1280
|
|
6316
|
+
} });
|
|
6317
|
+
const tempFilePath = this.createTempFile(payload, "headless://wxfile/temp/chosen-video-01.mp4");
|
|
6318
|
+
return {
|
|
6319
|
+
duration: Math.min(maxDuration, compressed ? 18 : 36),
|
|
6320
|
+
errMsg: "chooseVideo:ok",
|
|
6321
|
+
height: compressed ? 360 : 720,
|
|
6322
|
+
size: payload.length,
|
|
6323
|
+
tempFilePath,
|
|
6324
|
+
width: compressed ? 640 : 1280
|
|
6325
|
+
};
|
|
6326
|
+
},
|
|
6327
|
+
compressImage(option) {
|
|
6328
|
+
const fileContent = files.get(option.src);
|
|
6329
|
+
if (fileContent == null) throw new Error(`compressImage:fail file not found: ${option.src}`);
|
|
6330
|
+
const sourceSize = inferImageSize(fileContent);
|
|
6331
|
+
const sourceType = inferImageType(option.src, fileContent);
|
|
6332
|
+
const width = Number.isFinite(option.compressedWidth) ? Math.max(1, Math.trunc(option.compressedWidth ?? sourceSize.width)) : Math.max(1, sourceSize.width);
|
|
6333
|
+
const height = Number.isFinite(option.compressedHeight) ? Math.max(1, Math.trunc(option.compressedHeight ?? sourceSize.height)) : Math.max(1, sourceSize.height);
|
|
6334
|
+
const normalizedType = sourceType === "unknown" ? "jpg" : sourceType;
|
|
6335
|
+
const fileExtension = normalizedType === "jpeg" ? "jpg" : normalizedType;
|
|
6336
|
+
const payload = buildImageTempFilePayload(normalizedType, width, height, { quality: Number.isFinite(option.quality) ? Number(option.quality) : void 0 });
|
|
6337
|
+
return {
|
|
6338
|
+
errMsg: "compressImage:ok",
|
|
6339
|
+
tempFilePath: this.createTempFile(payload, `headless://wxfile/temp/compressed-image-${String(fileId + 1).padStart(2, "0")}.${fileExtension}`)
|
|
6340
|
+
};
|
|
6341
|
+
},
|
|
5008
6342
|
getNetworkType() {
|
|
5009
6343
|
return {
|
|
5010
6344
|
errMsg: "getNetworkType:ok",
|
|
5011
6345
|
networkType
|
|
5012
6346
|
};
|
|
5013
6347
|
},
|
|
6348
|
+
getImageInfo(option) {
|
|
6349
|
+
const fileContent = files.get(option.src);
|
|
6350
|
+
if (fileContent == null) throw new Error(`getImageInfo:fail file not found: ${option.src}`);
|
|
6351
|
+
const size = inferImageSize(fileContent);
|
|
6352
|
+
return {
|
|
6353
|
+
errMsg: "getImageInfo:ok",
|
|
6354
|
+
height: size.height,
|
|
6355
|
+
orientation: "up",
|
|
6356
|
+
path: option.src,
|
|
6357
|
+
type: inferImageType(option.src, fileContent),
|
|
6358
|
+
width: size.width
|
|
6359
|
+
};
|
|
6360
|
+
},
|
|
6361
|
+
getVideoInfo(option) {
|
|
6362
|
+
const fileContent = files.get(option.src);
|
|
6363
|
+
if (fileContent == null) throw new Error(`getVideoInfo:fail file not found: ${option.src}`);
|
|
6364
|
+
const info = inferVideoInfo(option.src, fileContent);
|
|
6365
|
+
return {
|
|
6366
|
+
bitrate: info.bitrate,
|
|
6367
|
+
duration: info.duration,
|
|
6368
|
+
errMsg: "getVideoInfo:ok",
|
|
6369
|
+
fps: info.fps,
|
|
6370
|
+
height: info.height,
|
|
6371
|
+
orientation: "up",
|
|
6372
|
+
size: info.size,
|
|
6373
|
+
type: info.type,
|
|
6374
|
+
width: info.width
|
|
6375
|
+
};
|
|
6376
|
+
},
|
|
5014
6377
|
getModalLogs() {
|
|
5015
6378
|
return modalLogs.map((entry) => ({
|
|
5016
6379
|
...entry,
|
|
@@ -5045,6 +6408,15 @@ function createHeadlessWxState() {
|
|
|
5045
6408
|
getStorageSnapshot() {
|
|
5046
6409
|
return Object.fromEntries(Array.from(storage.entries(), ([key, value]) => [key, cloneValue(value)]));
|
|
5047
6410
|
},
|
|
6411
|
+
getClipboardData() {
|
|
6412
|
+
return {
|
|
6413
|
+
data: clipboardData,
|
|
6414
|
+
errMsg: "getClipboardData:ok"
|
|
6415
|
+
};
|
|
6416
|
+
},
|
|
6417
|
+
getClipboardSnapshot() {
|
|
6418
|
+
return { data: clipboardData };
|
|
6419
|
+
},
|
|
5048
6420
|
getStorageSync(key) {
|
|
5049
6421
|
return cloneValue(storage.get(key));
|
|
5050
6422
|
},
|
|
@@ -5054,6 +6426,21 @@ function createHeadlessWxState() {
|
|
|
5054
6426
|
getLoading() {
|
|
5055
6427
|
return loading ? { ...loading } : null;
|
|
5056
6428
|
},
|
|
6429
|
+
getPreviewImage() {
|
|
6430
|
+
return previewImage ? {
|
|
6431
|
+
current: previewImage.current,
|
|
6432
|
+
urls: [...previewImage.urls],
|
|
6433
|
+
visible: previewImage.visible
|
|
6434
|
+
} : null;
|
|
6435
|
+
},
|
|
6436
|
+
getOpenedDocument() {
|
|
6437
|
+
return openedDocument ? {
|
|
6438
|
+
filePath: openedDocument.filePath,
|
|
6439
|
+
fileType: openedDocument.fileType,
|
|
6440
|
+
showMenu: openedDocument.showMenu,
|
|
6441
|
+
visible: openedDocument.visible
|
|
6442
|
+
} : null;
|
|
6443
|
+
},
|
|
5057
6444
|
hideShareMenu() {
|
|
5058
6445
|
shareMenu = {
|
|
5059
6446
|
...shareMenu,
|
|
@@ -5123,6 +6510,27 @@ function createHeadlessWxState() {
|
|
|
5123
6510
|
onNetworkStatusChange(callback) {
|
|
5124
6511
|
networkStatusChangeCallbacks.add(callback);
|
|
5125
6512
|
},
|
|
6513
|
+
openDocument(option) {
|
|
6514
|
+
const normalizedPath = normalizeFsPath(option.filePath);
|
|
6515
|
+
if (!files.has(normalizedPath)) throw new Error(`openDocument:fail no such file or directory, open '${normalizedPath}'`);
|
|
6516
|
+
openedDocument = {
|
|
6517
|
+
filePath: normalizedPath,
|
|
6518
|
+
fileType: typeof option.fileType === "string" && option.fileType.trim() !== "" ? normalizeFileExtension(option.fileType) : inferDocumentFileType(normalizedPath),
|
|
6519
|
+
showMenu: option.showMenu !== false,
|
|
6520
|
+
visible: true
|
|
6521
|
+
};
|
|
6522
|
+
return { errMsg: "openDocument:ok" };
|
|
6523
|
+
},
|
|
6524
|
+
previewImage(option) {
|
|
6525
|
+
const urls = Array.isArray(option.urls) ? option.urls.filter((item) => typeof item === "string" && item.trim() !== "") : [];
|
|
6526
|
+
if (urls.length === 0) throw new Error("previewImage:fail invalid urls");
|
|
6527
|
+
previewImage = {
|
|
6528
|
+
current: typeof option.current === "string" && option.current.trim() !== "" ? option.current : urls[0],
|
|
6529
|
+
urls: [...urls],
|
|
6530
|
+
visible: true
|
|
6531
|
+
};
|
|
6532
|
+
return { errMsg: "previewImage:ok" };
|
|
6533
|
+
},
|
|
5126
6534
|
removeStorageSync(key) {
|
|
5127
6535
|
storage.delete(key);
|
|
5128
6536
|
},
|
|
@@ -5170,6 +6578,14 @@ function createHeadlessWxState() {
|
|
|
5170
6578
|
removeSavedFile(option) {
|
|
5171
6579
|
return removeSavedFile(option.filePath);
|
|
5172
6580
|
},
|
|
6581
|
+
saveImageToPhotosAlbum(option) {
|
|
6582
|
+
if (files.get(option.filePath) == null) throw new Error(`saveImageToPhotosAlbum:fail file not found: ${option.filePath}`);
|
|
6583
|
+
return { errMsg: "saveImageToPhotosAlbum:ok" };
|
|
6584
|
+
},
|
|
6585
|
+
saveVideoToPhotosAlbum(option) {
|
|
6586
|
+
if (files.get(option.filePath) == null) throw new Error(`saveVideoToPhotosAlbum:fail file not found: ${option.filePath}`);
|
|
6587
|
+
return { errMsg: "saveVideoToPhotosAlbum:ok" };
|
|
6588
|
+
},
|
|
5173
6589
|
saveFile(option) {
|
|
5174
6590
|
const fileContent = files.get(option.tempFilePath);
|
|
5175
6591
|
if (fileContent == null) throw new Error(`saveFile:fail tempFilePath not found: ${option.tempFilePath}`);
|
|
@@ -5185,6 +6601,10 @@ function createHeadlessWxState() {
|
|
|
5185
6601
|
setStorageSync(key, value) {
|
|
5186
6602
|
storage.set(key, cloneValue(value));
|
|
5187
6603
|
},
|
|
6604
|
+
setClipboardData(option) {
|
|
6605
|
+
clipboardData = String(option.data);
|
|
6606
|
+
return { errMsg: "setClipboardData:ok" };
|
|
6607
|
+
},
|
|
5188
6608
|
setFile(filePath, fileContent) {
|
|
5189
6609
|
const normalizedPath = normalizeFsPath(filePath);
|
|
5190
6610
|
ensureDirectoryTree(normalizedPath);
|
|
@@ -5330,7 +6750,9 @@ function createHeadlessWxState() {
|
|
|
5330
6750
|
//#region ../../mpcore/packages/simulator/src/runtime/session.ts
|
|
5331
6751
|
const LEADING_SLASH_RE$1 = /^\/+/;
|
|
5332
6752
|
const PAGE_STACK_LIMIT = 10;
|
|
6753
|
+
const WHITESPACE_RE = /\s+/;
|
|
5333
6754
|
const DATA_ATTR_SELECTOR_RE = /^\[data-([^=\]]+)="([^"]*)"\]$/;
|
|
6755
|
+
const COMPOUND_SELECTOR_PART_RE = /#[\w-]+|\.[\w-]+|\[data-[^=\]]+="[^"]*"\]|[A-Za-z][\w-]*/g;
|
|
5334
6756
|
const DATASET_KEY_RE = /-([a-z])/g;
|
|
5335
6757
|
function stripLeadingSlash(route) {
|
|
5336
6758
|
return route.replace(LEADING_SLASH_RE$1, "");
|
|
@@ -5351,6 +6773,28 @@ function parseNavigationUrl(url) {
|
|
|
5351
6773
|
query: normalizeQuery(queryString)
|
|
5352
6774
|
};
|
|
5353
6775
|
}
|
|
6776
|
+
function parseCompoundSelector(selector) {
|
|
6777
|
+
const parts = selector.match(COMPOUND_SELECTOR_PART_RE) ?? [];
|
|
6778
|
+
return parts.join("") === selector ? parts : [];
|
|
6779
|
+
}
|
|
6780
|
+
function matchesComponentSelector(scope, selector) {
|
|
6781
|
+
const parts = parseCompoundSelector(selector);
|
|
6782
|
+
if (parts.length === 0) return false;
|
|
6783
|
+
return parts.every((part) => {
|
|
6784
|
+
if (part.startsWith("#")) return scope.id === part.slice(1);
|
|
6785
|
+
if (part.startsWith(".")) return scope.classList?.includes(part.slice(1)) ?? false;
|
|
6786
|
+
const dataAttrMatch = part.match(DATA_ATTR_SELECTOR_RE);
|
|
6787
|
+
if (dataAttrMatch) {
|
|
6788
|
+
const [, key, value] = dataAttrMatch;
|
|
6789
|
+
const datasetKey = key.replace(DATASET_KEY_RE, (_match, char) => char.toUpperCase());
|
|
6790
|
+
return scope.dataset?.[datasetKey] === value;
|
|
6791
|
+
}
|
|
6792
|
+
return scope.alias === part;
|
|
6793
|
+
});
|
|
6794
|
+
}
|
|
6795
|
+
function normalizeSelectorParts(selector) {
|
|
6796
|
+
return selector.trim().split(WHITESPACE_RE).filter(Boolean);
|
|
6797
|
+
}
|
|
5354
6798
|
function createAppLaunchOptions(pathname, query) {
|
|
5355
6799
|
return {
|
|
5356
6800
|
path: stripLeadingSlash(pathname),
|
|
@@ -5392,6 +6836,8 @@ var HeadlessSession = class {
|
|
|
5392
6836
|
tabBarState = /* @__PURE__ */ new Map();
|
|
5393
6837
|
tabBarVisible = false;
|
|
5394
6838
|
systemInfo = createDefaultSystemInfo();
|
|
6839
|
+
mediaQueryObservers = /* @__PURE__ */ new Set();
|
|
6840
|
+
canvasContexts = /* @__PURE__ */ new Map();
|
|
5395
6841
|
enterOptions = createAppLaunchOptions("", {});
|
|
5396
6842
|
launchOptions = createAppLaunchOptions("", {});
|
|
5397
6843
|
wxState = createHeadlessWxState();
|
|
@@ -5424,7 +6870,20 @@ var HeadlessSession = class {
|
|
|
5424
6870
|
});
|
|
5425
6871
|
this.tabBarVisible = this.tabBarRoutes.size > 0;
|
|
5426
6872
|
this.moduleLoader = createModuleLoader(this.registries, () => this.pages.slice(), () => this.getApp(), {
|
|
6873
|
+
chooseImage: (option) => this.wxState.chooseImage(option ?? {}),
|
|
6874
|
+
chooseMessageFile: (option) => this.wxState.chooseMessageFile(option ?? {}),
|
|
6875
|
+
chooseMedia: (option) => this.wxState.chooseMedia(option ?? {}),
|
|
6876
|
+
chooseVideo: (option) => this.wxState.chooseVideo(option ?? {}),
|
|
6877
|
+
compressImage: (option) => this.wxState.compressImage(option),
|
|
6878
|
+
createAnimation: (option) => this.createAnimation(option),
|
|
6879
|
+
createCanvasContext: (canvasId, scope) => this.createCanvasContext(canvasId, scope),
|
|
6880
|
+
canvasToTempFilePath: (option) => this.canvasToTempFilePath(option),
|
|
6881
|
+
createIntersectionObserver: (scope, options) => this.createIntersectionObserver(scope, options),
|
|
6882
|
+
createVideoContext: (videoId, scope) => this.createVideoContext(videoId, scope),
|
|
5427
6883
|
executeSelectorQuery: (requests, scope) => this.executeSelectorQuery(requests, scope),
|
|
6884
|
+
getFileInfo: (option) => this.wxState.getFileInfo(option),
|
|
6885
|
+
getImageInfo: (option) => this.wxState.getImageInfo(option),
|
|
6886
|
+
getVideoInfo: (option) => this.wxState.getVideoInfo(option),
|
|
5428
6887
|
getFileSystemManager: () => this.wxState.getFileSystemManager(),
|
|
5429
6888
|
getSavedFileInfo: (option) => this.wxState.getSavedFileInfo(option),
|
|
5430
6889
|
getSavedFileList: () => this.wxState.getSavedFileList(),
|
|
@@ -5445,11 +6904,13 @@ var HeadlessSession = class {
|
|
|
5445
6904
|
extraData: { ...this.launchOptions.referrerInfo.extraData }
|
|
5446
6905
|
}
|
|
5447
6906
|
}),
|
|
6907
|
+
getClipboardData: () => this.wxState.getClipboardData(),
|
|
5448
6908
|
getMenuButtonBoundingClientRect: () => deriveMenuButtonBoundingClientRect(this.systemInfo),
|
|
5449
6909
|
getNetworkType: () => this.wxState.getNetworkType(),
|
|
5450
6910
|
navigateBack: (option) => this.navigateBack(option?.delta),
|
|
5451
6911
|
navigateTo: (option) => this.navigateTo(option.url),
|
|
5452
6912
|
pageScrollTo: (option) => this.pageScrollTo(option),
|
|
6913
|
+
openDocument: (option) => this.wxState.openDocument(option),
|
|
5453
6914
|
clearStorageSync: () => this.wxState.clearStorageSync(),
|
|
5454
6915
|
getStorageInfoSync: () => this.wxState.getStorageInfoSync(),
|
|
5455
6916
|
getStorageSync: (key) => this.wxState.getStorageSync(key),
|
|
@@ -5461,14 +6922,18 @@ var HeadlessSession = class {
|
|
|
5461
6922
|
reLaunch: (option) => this.reLaunch(option.url),
|
|
5462
6923
|
redirectTo: (option) => this.redirectTo(option.url),
|
|
5463
6924
|
removeSavedFile: (option) => this.wxState.removeSavedFile(option),
|
|
6925
|
+
saveImageToPhotosAlbum: (option) => this.wxState.saveImageToPhotosAlbum(option),
|
|
6926
|
+
saveVideoToPhotosAlbum: (option) => this.wxState.saveVideoToPhotosAlbum(option),
|
|
5464
6927
|
nextTick: (callback) => queueMicrotask(() => callback?.()),
|
|
5465
6928
|
offNetworkStatusChange: (callback) => this.wxState.offNetworkStatusChange(callback),
|
|
5466
6929
|
onNetworkStatusChange: (callback) => this.wxState.onNetworkStatusChange(callback),
|
|
5467
6930
|
removeStorageSync: (key) => this.wxState.removeStorageSync(key),
|
|
6931
|
+
previewImage: (option) => this.wxState.previewImage(option),
|
|
5468
6932
|
request: (option) => this.wxState.request(option),
|
|
5469
6933
|
saveFile: (option) => this.wxState.saveFile(option),
|
|
5470
6934
|
setBackgroundColor: (option) => this.setBackgroundColor(option),
|
|
5471
6935
|
setBackgroundTextStyle: (option) => this.setBackgroundTextStyle(option.textStyle),
|
|
6936
|
+
setClipboardData: (option) => this.wxState.setClipboardData(option),
|
|
5472
6937
|
setStorageSync: (key, value) => this.wxState.setStorageSync(key, value),
|
|
5473
6938
|
setNavigationBarColor: (option) => this.setNavigationBarColor(option),
|
|
5474
6939
|
setNavigationBarTitle: (option) => this.setNavigationBarTitle(option.title),
|
|
@@ -5484,6 +6949,7 @@ var HeadlessSession = class {
|
|
|
5484
6949
|
showLoading: (option) => this.wxState.showLoading(option),
|
|
5485
6950
|
showModal: (option) => this.wxState.showModal(option),
|
|
5486
6951
|
showToast: (option) => this.wxState.showToast(option),
|
|
6952
|
+
startPullDownRefresh: () => this.startPullDownRefresh(),
|
|
5487
6953
|
stopPullDownRefresh: () => this.stopPullDownRefresh(),
|
|
5488
6954
|
switchTab: (option) => this.switchTab(option.url),
|
|
5489
6955
|
uploadFile: (option) => this.wxState.uploadFile(option),
|
|
@@ -5542,6 +7008,8 @@ var HeadlessSession = class {
|
|
|
5542
7008
|
moduleLoader: this.moduleLoader,
|
|
5543
7009
|
project: this.project,
|
|
5544
7010
|
session: {
|
|
7011
|
+
createIntersectionObserver: (scope, options) => this.createIntersectionObserver(scope, options),
|
|
7012
|
+
createMediaQueryObserver: (scope) => this.createMediaQueryObserver(scope),
|
|
5545
7013
|
selectAllComponentsWithin: (scopeId, selector) => this.selectAllComponentsWithin(scopeId, selector),
|
|
5546
7014
|
selectComponentWithin: (scopeId, selector) => this.selectComponentWithin(scopeId, selector),
|
|
5547
7015
|
selectOwnerComponent: (scopeId) => this.selectOwnerComponent(scopeId)
|
|
@@ -5609,6 +7077,7 @@ var HeadlessSession = class {
|
|
|
5609
7077
|
}
|
|
5610
7078
|
const instance = this.componentCache.get(normalizedScopeId);
|
|
5611
7079
|
if (!instance) throw new Error(`Unknown scope "${normalizedScopeId}" in headless runtime.`);
|
|
7080
|
+
instance.__lastInteractionEvent__ = void 0;
|
|
5612
7081
|
const method = instance[methodName];
|
|
5613
7082
|
if (typeof method !== "function") throw new TypeError(`Method "${methodName}" does not exist on headless component scope "${normalizedScopeId}".`);
|
|
5614
7083
|
return method.apply(instance, args);
|
|
@@ -5658,6 +7127,15 @@ var HeadlessSession = class {
|
|
|
5658
7127
|
getStorageInfo() {
|
|
5659
7128
|
return this.wxState.getStorageInfoSync();
|
|
5660
7129
|
}
|
|
7130
|
+
getClipboardData() {
|
|
7131
|
+
return this.wxState.getClipboardSnapshot();
|
|
7132
|
+
}
|
|
7133
|
+
getPreviewImage() {
|
|
7134
|
+
return this.wxState.getPreviewImage();
|
|
7135
|
+
}
|
|
7136
|
+
getOpenedDocument() {
|
|
7137
|
+
return this.wxState.getOpenedDocument();
|
|
7138
|
+
}
|
|
5661
7139
|
getPullDownRefreshState() {
|
|
5662
7140
|
return { ...this.pullDownRefreshState };
|
|
5663
7141
|
}
|
|
@@ -5931,7 +7409,8 @@ var HeadlessSession = class {
|
|
|
5931
7409
|
}
|
|
5932
7410
|
pageScrollTo(option) {
|
|
5933
7411
|
const current = this.requireCurrentPage("wx.pageScrollTo()");
|
|
5934
|
-
|
|
7412
|
+
const selectorScrollTop = resolveSelectorScrollTop(this.renderCurrentPage().root, option.selector);
|
|
7413
|
+
current.__scrollTop__ = Number(selectorScrollTop ?? option.scrollTop ?? 0);
|
|
5935
7414
|
current.onPageScroll?.({ scrollTop: current.__scrollTop__ });
|
|
5936
7415
|
}
|
|
5937
7416
|
triggerPullDownRefresh() {
|
|
@@ -5943,6 +7422,10 @@ var HeadlessSession = class {
|
|
|
5943
7422
|
current.onPullDownRefresh?.();
|
|
5944
7423
|
return current;
|
|
5945
7424
|
}
|
|
7425
|
+
startPullDownRefresh() {
|
|
7426
|
+
this.triggerPullDownRefresh();
|
|
7427
|
+
return { errMsg: "startPullDownRefresh:ok" };
|
|
7428
|
+
}
|
|
5946
7429
|
triggerReachBottom() {
|
|
5947
7430
|
const current = this.requireCurrentPage("triggerReachBottom()");
|
|
5948
7431
|
current.onReachBottom?.();
|
|
@@ -5953,6 +7436,7 @@ var HeadlessSession = class {
|
|
|
5953
7436
|
applyResizeToSystemInfo(this.systemInfo, options);
|
|
5954
7437
|
current.onResize?.(options);
|
|
5955
7438
|
this.runPageComponentLifetime(current.route, "resize", options);
|
|
7439
|
+
this.notifyMediaQueryObservers();
|
|
5956
7440
|
return current;
|
|
5957
7441
|
}
|
|
5958
7442
|
triggerRouteDone(options = {}) {
|
|
@@ -5973,6 +7457,11 @@ var HeadlessSession = class {
|
|
|
5973
7457
|
const scopeId = scope && scope !== current ? this.getComponentScopeId(scope) : null;
|
|
5974
7458
|
return executeSelectorQueryRequests(requests, {
|
|
5975
7459
|
page: current,
|
|
7460
|
+
resolveContext: (node) => {
|
|
7461
|
+
if (node.name !== "canvas") return { type: "unsupported-context" };
|
|
7462
|
+
const canvasId = node.attribs?.["canvas-id"];
|
|
7463
|
+
return typeof canvasId === "string" && canvasId ? this.createCanvasContext(canvasId, scope) : { type: "unsupported-context" };
|
|
7464
|
+
},
|
|
5976
7465
|
root: resolveSelectorQueryScopeRoot(rendered.root, scopeId),
|
|
5977
7466
|
windowInfo: this.getWindowInfo()
|
|
5978
7467
|
});
|
|
@@ -5986,6 +7475,8 @@ var HeadlessSession = class {
|
|
|
5986
7475
|
background: resolveBackgroundSnapshot(this.project.appConfig, pageConfig),
|
|
5987
7476
|
navigationBar: resolveNavigationBarSnapshot(this.project.appConfig, pageConfig)
|
|
5988
7477
|
});
|
|
7478
|
+
pageInstance.createIntersectionObserver = (options) => this.createIntersectionObserver(pageInstance, options);
|
|
7479
|
+
pageInstance.createMediaQueryObserver = () => this.createMediaQueryObserver(pageInstance);
|
|
5989
7480
|
pageInstance.selectComponent = (selector) => this.selectComponent(selector);
|
|
5990
7481
|
pageInstance.selectAllComponents = (selector) => this.selectAllComponents(selector);
|
|
5991
7482
|
pageInstance.onLoad?.(target.query);
|
|
@@ -6035,10 +7526,12 @@ var HeadlessSession = class {
|
|
|
6035
7526
|
this.tabPages.clear();
|
|
6036
7527
|
this.componentCache.clear();
|
|
6037
7528
|
this.componentScopes.clear();
|
|
7529
|
+
this.clearMediaQueryObservers();
|
|
6038
7530
|
this.currentPageInstance = null;
|
|
6039
7531
|
}
|
|
6040
7532
|
unloadPage(page) {
|
|
6041
7533
|
page.onUnload?.();
|
|
7534
|
+
this.clearMediaQueryObservers(page);
|
|
6042
7535
|
this.detachPageComponents(page.route);
|
|
6043
7536
|
this.tabPages.delete(stripLeadingSlash(page.route));
|
|
6044
7537
|
}
|
|
@@ -6049,21 +7542,21 @@ var HeadlessSession = class {
|
|
|
6049
7542
|
return current;
|
|
6050
7543
|
}
|
|
6051
7544
|
selectComponentsWithin(rootScopeId, selector) {
|
|
6052
|
-
const
|
|
6053
|
-
if (
|
|
7545
|
+
const selectorParts = normalizeSelectorParts(selector);
|
|
7546
|
+
if (selectorParts.length === 0) return [];
|
|
6054
7547
|
return [...this.componentScopes.entries()].filter(([candidateScopeId, scope]) => {
|
|
6055
7548
|
if (!candidateScopeId.includes("/")) return false;
|
|
6056
7549
|
if (rootScopeId && candidateScopeId === rootScopeId) return false;
|
|
6057
7550
|
if (rootScopeId && !candidateScopeId.startsWith(`${rootScopeId}/`)) return false;
|
|
6058
|
-
|
|
6059
|
-
if (
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
7551
|
+
const lastPart = selectorParts.at(-1);
|
|
7552
|
+
if (!lastPart || !matchesComponentSelector(scope, lastPart)) return false;
|
|
7553
|
+
let currentScope = scope.ownerScopeId ? this.componentScopes.get(scope.ownerScopeId) : void 0;
|
|
7554
|
+
for (let partIndex = selectorParts.length - 2; partIndex >= 0; partIndex -= 1) {
|
|
7555
|
+
while (currentScope && !matchesComponentSelector(currentScope, selectorParts[partIndex])) currentScope = currentScope.ownerScopeId ? this.componentScopes.get(currentScope.ownerScopeId) : void 0;
|
|
7556
|
+
if (!currentScope) return false;
|
|
7557
|
+
currentScope = currentScope.ownerScopeId ? this.componentScopes.get(currentScope.ownerScopeId) : void 0;
|
|
6065
7558
|
}
|
|
6066
|
-
return
|
|
7559
|
+
return true;
|
|
6067
7560
|
}).map(([candidateScopeId]) => this.componentCache.get(candidateScopeId)).filter(Boolean);
|
|
6068
7561
|
}
|
|
6069
7562
|
detachPageComponents(route) {
|
|
@@ -6075,6 +7568,18 @@ var HeadlessSession = class {
|
|
|
6075
7568
|
this.componentScopes.delete(scopeId);
|
|
6076
7569
|
}
|
|
6077
7570
|
}
|
|
7571
|
+
notifyMediaQueryObservers() {
|
|
7572
|
+
for (const entry of [...this.mediaQueryObservers]) {
|
|
7573
|
+
if (entry.ownerPage !== this.currentPageInstance) continue;
|
|
7574
|
+
entry.notify();
|
|
7575
|
+
}
|
|
7576
|
+
}
|
|
7577
|
+
clearMediaQueryObservers(ownerPage) {
|
|
7578
|
+
for (const entry of [...this.mediaQueryObservers]) {
|
|
7579
|
+
if (ownerPage && entry.ownerPage !== ownerPage) continue;
|
|
7580
|
+
entry.disconnect();
|
|
7581
|
+
}
|
|
7582
|
+
}
|
|
6078
7583
|
runPageComponentLifetime(route, lifetimeName, payload) {
|
|
6079
7584
|
const prefix = `page:${stripLeadingSlash(route)}`;
|
|
6080
7585
|
for (const [scopeId, instance] of this.componentCache.entries()) {
|
|
@@ -6086,6 +7591,100 @@ var HeadlessSession = class {
|
|
|
6086
7591
|
for (const [scopeId, instance] of this.componentCache.entries()) if (instance === component) return scopeId;
|
|
6087
7592
|
return null;
|
|
6088
7593
|
}
|
|
7594
|
+
createVideoContext(videoId, scope) {
|
|
7595
|
+
return createHeadlessVideoContext({
|
|
7596
|
+
callScopeMethod: (scopeId, methodName, event) => this.callScopeMethod(scopeId, methodName, event),
|
|
7597
|
+
renderCurrentPage: () => this.renderCurrentPage(),
|
|
7598
|
+
resolveScope: (value) => {
|
|
7599
|
+
const current = this.currentPageInstance;
|
|
7600
|
+
if (!value || value === current) return { kind: "page" };
|
|
7601
|
+
const scopeId = this.getScopeIdForComponent(value);
|
|
7602
|
+
if (!scopeId) return { kind: "missing" };
|
|
7603
|
+
return {
|
|
7604
|
+
kind: "component",
|
|
7605
|
+
scopeId
|
|
7606
|
+
};
|
|
7607
|
+
}
|
|
7608
|
+
}, videoId, scope);
|
|
7609
|
+
}
|
|
7610
|
+
createAnimation(option) {
|
|
7611
|
+
return createHeadlessAnimation(option);
|
|
7612
|
+
}
|
|
7613
|
+
resolveCanvasScopeKey(scope) {
|
|
7614
|
+
const current = this.currentPageInstance;
|
|
7615
|
+
if (!scope || scope === current) return "page";
|
|
7616
|
+
const scopeId = this.getScopeIdForComponent(scope);
|
|
7617
|
+
return scopeId ? `component:${scopeId}` : "missing";
|
|
7618
|
+
}
|
|
7619
|
+
createCanvasContext(canvasId, scope) {
|
|
7620
|
+
const context = createHeadlessCanvasContext({
|
|
7621
|
+
renderCurrentPage: () => this.renderCurrentPage(),
|
|
7622
|
+
resolveScope: (value) => {
|
|
7623
|
+
const current = this.currentPageInstance;
|
|
7624
|
+
if (!value || value === current) return { kind: "page" };
|
|
7625
|
+
const scopeId = this.getScopeIdForComponent(value);
|
|
7626
|
+
if (!scopeId) return { kind: "missing" };
|
|
7627
|
+
return {
|
|
7628
|
+
kind: "component",
|
|
7629
|
+
scopeId
|
|
7630
|
+
};
|
|
7631
|
+
}
|
|
7632
|
+
}, canvasId, scope);
|
|
7633
|
+
this.canvasContexts.set(`${this.resolveCanvasScopeKey(scope)}:${canvasId}`, context);
|
|
7634
|
+
return context;
|
|
7635
|
+
}
|
|
7636
|
+
canvasToTempFilePath(option) {
|
|
7637
|
+
const context = this.canvasContexts.get(`${this.resolveCanvasScopeKey(option.component)}:${option.canvasId}`);
|
|
7638
|
+
if (!context) throw new Error(`canvasToTempFilePath:fail canvas "${option.canvasId}" has not been drawn in headless runtime.`);
|
|
7639
|
+
return {
|
|
7640
|
+
errMsg: "canvasToTempFilePath:ok",
|
|
7641
|
+
tempFilePath: this.wxState.createTempFile(JSON.stringify({
|
|
7642
|
+
canvasId: option.canvasId,
|
|
7643
|
+
config: {
|
|
7644
|
+
destHeight: option.destHeight,
|
|
7645
|
+
destWidth: option.destWidth,
|
|
7646
|
+
fileType: option.fileType,
|
|
7647
|
+
height: option.height,
|
|
7648
|
+
quality: option.quality,
|
|
7649
|
+
width: option.width,
|
|
7650
|
+
x: option.x,
|
|
7651
|
+
y: option.y
|
|
7652
|
+
},
|
|
7653
|
+
snapshot: context.__getSnapshot()
|
|
7654
|
+
}))
|
|
7655
|
+
};
|
|
7656
|
+
}
|
|
7657
|
+
createIntersectionObserver(scope, options) {
|
|
7658
|
+
return createHeadlessIntersectionObserver({
|
|
7659
|
+
getWindowInfo: () => this.getWindowInfo(),
|
|
7660
|
+
renderCurrentPage: () => this.renderCurrentPage(),
|
|
7661
|
+
resolveScope: (value) => {
|
|
7662
|
+
const current = this.currentPageInstance;
|
|
7663
|
+
if (!value || value === current) return { kind: "page" };
|
|
7664
|
+
const scopeId = this.getScopeIdForComponent(value);
|
|
7665
|
+
if (!scopeId) return { kind: "missing" };
|
|
7666
|
+
return {
|
|
7667
|
+
kind: "component",
|
|
7668
|
+
scopeId
|
|
7669
|
+
};
|
|
7670
|
+
}
|
|
7671
|
+
}, scope, options);
|
|
7672
|
+
}
|
|
7673
|
+
createMediaQueryObserver(scope) {
|
|
7674
|
+
const current = this.requireCurrentPage("createMediaQueryObserver()");
|
|
7675
|
+
const ownerPage = scope === current || !scope ? current : this.currentPageInstance ?? current;
|
|
7676
|
+
let entry;
|
|
7677
|
+
const controller = createHeadlessMediaQueryObserver({ getWindowInfo: () => this.getWindowInfo() }, () => {
|
|
7678
|
+
this.mediaQueryObservers.delete(entry);
|
|
7679
|
+
});
|
|
7680
|
+
entry = {
|
|
7681
|
+
disconnect: controller.disconnect,
|
|
7682
|
+
notify: controller.notify,
|
|
7683
|
+
ownerPage
|
|
7684
|
+
};
|
|
7685
|
+
this.mediaQueryObservers.add(entry);
|
|
7686
|
+
return controller.observer;
|
|
7687
|
+
}
|
|
6089
7688
|
};
|
|
6090
7689
|
function createHeadlessSession(options) {
|
|
6091
7690
|
return new HeadlessSession(options);
|