@skrillex1224/playwright-toolkit 2.1.226 → 2.1.228
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 +2 -0
- package/dist/browser.js.map +2 -2
- package/dist/index.cjs +623 -93
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +623 -93
- package/dist/index.js.map +4 -4
- package/index.d.ts +39 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -439,6 +439,9 @@ function createInternalLogger(moduleName, explicitLogger) {
|
|
|
439
439
|
};
|
|
440
440
|
}
|
|
441
441
|
|
|
442
|
+
// src/internals/screenshot.js
|
|
443
|
+
import delay from "delay";
|
|
444
|
+
|
|
442
445
|
// src/internals/viewport.js
|
|
443
446
|
var toPositiveInt = (value) => {
|
|
444
447
|
const number = Math.round(Number(value) || 0);
|
|
@@ -472,11 +475,18 @@ var DEFAULT_TIMEOUT_MS = 5e3;
|
|
|
472
475
|
var FORCED_FULLPAGE_TYPE = "jpeg";
|
|
473
476
|
var FORCED_FULLPAGE_QUALITY = 50;
|
|
474
477
|
var SUPPORTED_TYPES = /* @__PURE__ */ new Set(["png", "jpeg", "webp"]);
|
|
478
|
+
var EXPANDED_SCROLLABLE_CLASS = "__pk_expanded__";
|
|
479
|
+
var DEFAULT_MAX_HEIGHT = 8e3;
|
|
480
|
+
var DEFAULT_SETTLE_MS = 1e3;
|
|
475
481
|
var toPositiveNumber = (value, fallback = 0) => {
|
|
476
482
|
const n = Number(value);
|
|
477
483
|
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
478
484
|
return n;
|
|
479
485
|
};
|
|
486
|
+
var toPositiveInteger = (value, fallback = 0) => {
|
|
487
|
+
const number = Math.round(Number(value) || 0);
|
|
488
|
+
return number > 0 ? number : fallback;
|
|
489
|
+
};
|
|
480
490
|
var normalizeType = (value) => {
|
|
481
491
|
const raw = String(value || "png").trim().toLowerCase();
|
|
482
492
|
if (!SUPPORTED_TYPES.has(raw)) return "png";
|
|
@@ -505,6 +515,138 @@ var buildFullPageClip = (metrics, viewport, maxClipHeight) => {
|
|
|
505
515
|
scale: 1
|
|
506
516
|
};
|
|
507
517
|
};
|
|
518
|
+
var expandScrollableContent = async (page, options = {}) => {
|
|
519
|
+
return await page.evaluate(({ className, forceScrollableHeight, visibleOnly }) => {
|
|
520
|
+
const body = document.body;
|
|
521
|
+
const root = document.documentElement;
|
|
522
|
+
const scrollingElement = document.scrollingElement;
|
|
523
|
+
const candidates = [];
|
|
524
|
+
const seen = /* @__PURE__ */ new Set();
|
|
525
|
+
let maxHeight = window.innerHeight || 0;
|
|
526
|
+
const pushCandidate = (el) => {
|
|
527
|
+
if (!el || seen.has(el)) return;
|
|
528
|
+
seen.add(el);
|
|
529
|
+
candidates.push(el);
|
|
530
|
+
};
|
|
531
|
+
pushCandidate(scrollingElement);
|
|
532
|
+
pushCandidate(root);
|
|
533
|
+
pushCandidate(body);
|
|
534
|
+
document.querySelectorAll("*").forEach(pushCandidate);
|
|
535
|
+
const isDocumentElement = (el) => el === root || el === body || el === scrollingElement;
|
|
536
|
+
const isVisible = (el, style, rect) => {
|
|
537
|
+
if (!el || !style || !rect) return false;
|
|
538
|
+
if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse") {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
if (Number(style.opacity) === 0) return false;
|
|
542
|
+
return rect.width > 0 && rect.height > 0;
|
|
543
|
+
};
|
|
544
|
+
const isScrollableY = (el, style) => {
|
|
545
|
+
if (!el || el.scrollHeight <= el.clientHeight + 1) {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
if (isDocumentElement(el)) {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
const overflowY = String(style.overflowY || "").toLowerCase();
|
|
552
|
+
const overflow = String(style.overflow || "").toLowerCase();
|
|
553
|
+
const scrollableOverflow = /* @__PURE__ */ new Set(["auto", "scroll", "overlay"]);
|
|
554
|
+
return scrollableOverflow.has(overflowY) || scrollableOverflow.has(overflow);
|
|
555
|
+
};
|
|
556
|
+
candidates.forEach((el) => {
|
|
557
|
+
const style = window.getComputedStyle(el);
|
|
558
|
+
const rect = el.getBoundingClientRect();
|
|
559
|
+
if (visibleOnly && !isVisible(el, style, rect)) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (!isScrollableY(el, style)) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const scrollHeight = Math.ceil(el.scrollHeight || 0);
|
|
566
|
+
if (scrollHeight <= 0) {
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
if (scrollHeight > maxHeight) {
|
|
570
|
+
maxHeight = scrollHeight;
|
|
571
|
+
}
|
|
572
|
+
if (isDocumentElement(el)) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (!el.classList.contains(className)) {
|
|
576
|
+
el.dataset.pkOrigOverflow = el.style.overflow;
|
|
577
|
+
el.dataset.pkOrigOverflowPriority = el.style.getPropertyPriority("overflow") || "";
|
|
578
|
+
el.dataset.pkOrigHeight = el.style.height;
|
|
579
|
+
el.dataset.pkOrigHeightPriority = el.style.getPropertyPriority("height") || "";
|
|
580
|
+
el.dataset.pkOrigMinHeight = el.style.minHeight;
|
|
581
|
+
el.dataset.pkOrigMinHeightPriority = el.style.getPropertyPriority("min-height") || "";
|
|
582
|
+
el.dataset.pkOrigMaxHeight = el.style.maxHeight;
|
|
583
|
+
el.dataset.pkOrigMaxHeightPriority = el.style.getPropertyPriority("max-height") || "";
|
|
584
|
+
el.classList.add(className);
|
|
585
|
+
}
|
|
586
|
+
if (forceScrollableHeight) {
|
|
587
|
+
el.style.setProperty("overflow", "visible", "important");
|
|
588
|
+
el.style.setProperty("height", `${scrollHeight}px`, "important");
|
|
589
|
+
el.style.setProperty("min-height", `${scrollHeight}px`, "important");
|
|
590
|
+
el.style.setProperty("max-height", "none", "important");
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
el.style.overflow = "visible";
|
|
594
|
+
el.style.height = "auto";
|
|
595
|
+
el.style.maxHeight = "none";
|
|
596
|
+
});
|
|
597
|
+
return maxHeight;
|
|
598
|
+
}, {
|
|
599
|
+
className: EXPANDED_SCROLLABLE_CLASS,
|
|
600
|
+
forceScrollableHeight: Boolean(options.forceScrollableHeight),
|
|
601
|
+
visibleOnly: options.visibleOnly !== false
|
|
602
|
+
});
|
|
603
|
+
};
|
|
604
|
+
var prepareExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
605
|
+
const originalViewport = await resolveCurrentViewportSize(page);
|
|
606
|
+
const defaultBuffer = Math.round((originalViewport.height || 1080) / 2);
|
|
607
|
+
const buffer = options.buffer ?? defaultBuffer;
|
|
608
|
+
const maxHeight = toPositiveInteger(options.maxHeight, DEFAULT_MAX_HEIGHT);
|
|
609
|
+
const settleMs = Math.max(0, Number(options.settleMs ?? DEFAULT_SETTLE_MS) || 0);
|
|
610
|
+
const maxScrollHeight = await expandScrollableContent(page, {
|
|
611
|
+
forceScrollableHeight: options.forceScrollableHeight,
|
|
612
|
+
visibleOnly: options.visibleOnly !== false
|
|
613
|
+
});
|
|
614
|
+
const targetHeight = Math.min(maxScrollHeight + buffer, maxHeight);
|
|
615
|
+
await page.setViewportSize({
|
|
616
|
+
width: originalViewport.width,
|
|
617
|
+
height: targetHeight
|
|
618
|
+
});
|
|
619
|
+
if (settleMs > 0) {
|
|
620
|
+
await delay(settleMs);
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
originalViewport,
|
|
624
|
+
maxScrollHeight,
|
|
625
|
+
targetHeight
|
|
626
|
+
};
|
|
627
|
+
};
|
|
628
|
+
var restoreExpandedFullPageScreenshot = async (page, state = {}) => {
|
|
629
|
+
await page.evaluate((className) => {
|
|
630
|
+
document.querySelectorAll(`.${className}`).forEach((el) => {
|
|
631
|
+
el.style.setProperty("overflow", el.dataset.pkOrigOverflow || "", el.dataset.pkOrigOverflowPriority || "");
|
|
632
|
+
el.style.setProperty("height", el.dataset.pkOrigHeight || "", el.dataset.pkOrigHeightPriority || "");
|
|
633
|
+
el.style.setProperty("min-height", el.dataset.pkOrigMinHeight || "", el.dataset.pkOrigMinHeightPriority || "");
|
|
634
|
+
el.style.setProperty("max-height", el.dataset.pkOrigMaxHeight || "", el.dataset.pkOrigMaxHeightPriority || "");
|
|
635
|
+
delete el.dataset.pkOrigOverflow;
|
|
636
|
+
delete el.dataset.pkOrigOverflowPriority;
|
|
637
|
+
delete el.dataset.pkOrigHeight;
|
|
638
|
+
delete el.dataset.pkOrigHeightPriority;
|
|
639
|
+
delete el.dataset.pkOrigMinHeight;
|
|
640
|
+
delete el.dataset.pkOrigMinHeightPriority;
|
|
641
|
+
delete el.dataset.pkOrigMaxHeight;
|
|
642
|
+
delete el.dataset.pkOrigMaxHeightPriority;
|
|
643
|
+
el.classList.remove(className);
|
|
644
|
+
});
|
|
645
|
+
}, EXPANDED_SCROLLABLE_CLASS);
|
|
646
|
+
if (state?.originalViewport) {
|
|
647
|
+
await page.setViewportSize(state.originalViewport);
|
|
648
|
+
}
|
|
649
|
+
};
|
|
508
650
|
var capturePageScreenshot = async (page, options = {}) => {
|
|
509
651
|
const fullPage = Boolean(options.fullPage);
|
|
510
652
|
const type = fullPage ? FORCED_FULLPAGE_TYPE : normalizeType(options.type);
|
|
@@ -558,6 +700,22 @@ var capturePageScreenshot = async (page, options = {}) => {
|
|
|
558
700
|
return await page.screenshot(fallbackOptions);
|
|
559
701
|
}
|
|
560
702
|
};
|
|
703
|
+
var captureExpandedFullPageScreenshot = async (page, options = {}) => {
|
|
704
|
+
const state = await prepareExpandedFullPageScreenshot(page, options);
|
|
705
|
+
try {
|
|
706
|
+
return await capturePageScreenshot(page, {
|
|
707
|
+
fullPage: true,
|
|
708
|
+
type: options.type || "png",
|
|
709
|
+
quality: options.quality,
|
|
710
|
+
timeout: options.timeout,
|
|
711
|
+
maxClipHeight: state.targetHeight
|
|
712
|
+
});
|
|
713
|
+
} finally {
|
|
714
|
+
if (options.restore) {
|
|
715
|
+
await restoreExpandedFullPageScreenshot(page, state);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
};
|
|
561
719
|
|
|
562
720
|
// src/errors.js
|
|
563
721
|
var errors_exports = {};
|
|
@@ -2096,7 +2254,7 @@ var AntiCheat = {
|
|
|
2096
2254
|
};
|
|
2097
2255
|
|
|
2098
2256
|
// src/internals/humanize/desktop.js
|
|
2099
|
-
import
|
|
2257
|
+
import delay2 from "delay";
|
|
2100
2258
|
import { createCursor } from "ghost-cursor-playwright";
|
|
2101
2259
|
var logger6 = createInternalLogger("Humanize");
|
|
2102
2260
|
var $CursorWeakMap = /* @__PURE__ */ new WeakMap();
|
|
@@ -2328,7 +2486,7 @@ var Humanize = {
|
|
|
2328
2486
|
}
|
|
2329
2487
|
await page.mouse.wheel(0, deltaY);
|
|
2330
2488
|
didScroll = true;
|
|
2331
|
-
await
|
|
2489
|
+
await delay2(this.jitterMs(20 + Math.random() * 40, 0.2));
|
|
2332
2490
|
}
|
|
2333
2491
|
logger6.warn(`humanScroll | \u5728 ${maxSteps} \u6B65\u540E\u65E0\u6CD5\u786E\u4FDD\u53EF\u89C1\u6027`);
|
|
2334
2492
|
return { element, didScroll };
|
|
@@ -2357,7 +2515,7 @@ var Humanize = {
|
|
|
2357
2515
|
restoreOnce.restored = true;
|
|
2358
2516
|
if (typeof restoreOnce.do !== "function") return;
|
|
2359
2517
|
try {
|
|
2360
|
-
await
|
|
2518
|
+
await delay2(this.jitterMs(1e3));
|
|
2361
2519
|
await restoreOnce.do();
|
|
2362
2520
|
} catch (restoreError) {
|
|
2363
2521
|
logger6.warn(`humanClick: \u6062\u590D\u6EDA\u52A8\u4F4D\u7F6E\u5931\u8D25: ${restoreError.message}`);
|
|
@@ -2365,7 +2523,7 @@ var Humanize = {
|
|
|
2365
2523
|
};
|
|
2366
2524
|
try {
|
|
2367
2525
|
if (target == null) {
|
|
2368
|
-
await
|
|
2526
|
+
await delay2(this.jitterMs(reactionDelay, 0.4));
|
|
2369
2527
|
await cursor.actions.click();
|
|
2370
2528
|
logger6.success("humanClick", "Clicked current position");
|
|
2371
2529
|
return true;
|
|
@@ -2399,7 +2557,7 @@ var Humanize = {
|
|
|
2399
2557
|
const x = box.x + box.width / 2 + (Math.random() - 0.5) * box.width * 0.3;
|
|
2400
2558
|
const y = box.y + box.height / 2 + (Math.random() - 0.5) * box.height * 0.3;
|
|
2401
2559
|
await cursor.actions.move({ x, y });
|
|
2402
|
-
await
|
|
2560
|
+
await delay2(this.jitterMs(reactionDelay, 0.4));
|
|
2403
2561
|
await cursor.actions.click();
|
|
2404
2562
|
await restoreOnce();
|
|
2405
2563
|
logger6.success("humanClick");
|
|
@@ -2418,7 +2576,7 @@ var Humanize = {
|
|
|
2418
2576
|
async randomSleep(baseMs, jitterPercent = 0.3) {
|
|
2419
2577
|
const ms = this.jitterMs(baseMs, jitterPercent);
|
|
2420
2578
|
logger6.start("randomSleep", `base=${baseMs}, actual=${ms}ms`);
|
|
2421
|
-
await
|
|
2579
|
+
await delay2(ms);
|
|
2422
2580
|
logger6.success("randomSleep");
|
|
2423
2581
|
},
|
|
2424
2582
|
/**
|
|
@@ -2436,7 +2594,7 @@ var Humanize = {
|
|
|
2436
2594
|
const x = 100 + Math.random() * (viewportSize.width - 200);
|
|
2437
2595
|
const y = 100 + Math.random() * (viewportSize.height - 200);
|
|
2438
2596
|
await cursor.actions.move({ x, y });
|
|
2439
|
-
await
|
|
2597
|
+
await delay2(this.jitterMs(600, 0.5));
|
|
2440
2598
|
}
|
|
2441
2599
|
logger6.success("simulateGaze");
|
|
2442
2600
|
},
|
|
@@ -2460,7 +2618,7 @@ var Humanize = {
|
|
|
2460
2618
|
try {
|
|
2461
2619
|
const locator = page.locator(selector);
|
|
2462
2620
|
await Humanize.humanClick(page, locator);
|
|
2463
|
-
await
|
|
2621
|
+
await delay2(this.jitterMs(200, 0.4));
|
|
2464
2622
|
for (let i = 0; i < text.length; i++) {
|
|
2465
2623
|
const char = text[i];
|
|
2466
2624
|
let charDelay;
|
|
@@ -2472,11 +2630,11 @@ var Humanize = {
|
|
|
2472
2630
|
charDelay = this.jitterMs(baseDelay, 0.4);
|
|
2473
2631
|
}
|
|
2474
2632
|
await page.keyboard.type(char);
|
|
2475
|
-
await
|
|
2633
|
+
await delay2(charDelay);
|
|
2476
2634
|
if (Math.random() < pauseProbability && i < text.length - 1) {
|
|
2477
2635
|
const pauseTime = this.jitterMs(pauseBase, 0.5);
|
|
2478
2636
|
logger6.debug(`\u505C\u987F ${pauseTime}ms...`);
|
|
2479
|
-
await
|
|
2637
|
+
await delay2(pauseTime);
|
|
2480
2638
|
}
|
|
2481
2639
|
}
|
|
2482
2640
|
logger6.success("humanType");
|
|
@@ -2485,6 +2643,43 @@ var Humanize = {
|
|
|
2485
2643
|
throw error;
|
|
2486
2644
|
}
|
|
2487
2645
|
},
|
|
2646
|
+
/**
|
|
2647
|
+
* 人类化按键 - 模拟用户在当前焦点或指定目标上按下一次键。
|
|
2648
|
+
* @param {import('playwright').Page} page
|
|
2649
|
+
* @param {string|import('playwright').ElementHandle|import('playwright').Locator} targetOrKey - 目标或按键
|
|
2650
|
+
* @param {string|Object} [maybeKey] - 按键或选项
|
|
2651
|
+
* @param {Object} [options]
|
|
2652
|
+
*/
|
|
2653
|
+
async humanPress(page, targetOrKey, maybeKey, options = {}) {
|
|
2654
|
+
const hasTarget = typeof maybeKey === "string";
|
|
2655
|
+
const key = hasTarget ? maybeKey : targetOrKey;
|
|
2656
|
+
const pressOptions = hasTarget ? options : maybeKey || options;
|
|
2657
|
+
const {
|
|
2658
|
+
reactionDelay = 180,
|
|
2659
|
+
holdDelay = 45,
|
|
2660
|
+
focusDelay = 180,
|
|
2661
|
+
scrollIfNeeded = true,
|
|
2662
|
+
throwOnMissing = true,
|
|
2663
|
+
keyboardOptions = {}
|
|
2664
|
+
} = pressOptions || {};
|
|
2665
|
+
const targetDesc = hasTarget ? typeof targetOrKey === "string" ? targetOrKey : "ElementHandle" : "current focus";
|
|
2666
|
+
logger6.start("humanPress", `key=${key}, target=${targetDesc}`);
|
|
2667
|
+
try {
|
|
2668
|
+
if (hasTarget) {
|
|
2669
|
+
await this.humanClick(page, targetOrKey, { reactionDelay: focusDelay, scrollIfNeeded, throwOnMissing });
|
|
2670
|
+
}
|
|
2671
|
+
await delay2(this.jitterMs(reactionDelay, 0.45));
|
|
2672
|
+
await page.keyboard.press(key, {
|
|
2673
|
+
...keyboardOptions,
|
|
2674
|
+
delay: this.jitterMs(holdDelay, 0.5)
|
|
2675
|
+
});
|
|
2676
|
+
logger6.success("humanPress");
|
|
2677
|
+
return true;
|
|
2678
|
+
} catch (error) {
|
|
2679
|
+
logger6.fail("humanPress", error);
|
|
2680
|
+
throw error;
|
|
2681
|
+
}
|
|
2682
|
+
},
|
|
2488
2683
|
/**
|
|
2489
2684
|
* 人类化清空输入框 - 模拟人类删除文本的行为
|
|
2490
2685
|
* @param {import('playwright').Page} page
|
|
@@ -2495,14 +2690,14 @@ var Humanize = {
|
|
|
2495
2690
|
try {
|
|
2496
2691
|
const locator = page.locator(selector);
|
|
2497
2692
|
await locator.click();
|
|
2498
|
-
await
|
|
2693
|
+
await delay2(this.jitterMs(200, 0.4));
|
|
2499
2694
|
const currentValue = await locator.inputValue();
|
|
2500
2695
|
if (!currentValue || currentValue.length === 0) {
|
|
2501
2696
|
logger6.success("humanClear", "already empty");
|
|
2502
2697
|
return;
|
|
2503
2698
|
}
|
|
2504
2699
|
await page.keyboard.press("Meta+A");
|
|
2505
|
-
await
|
|
2700
|
+
await delay2(this.jitterMs(100, 0.4));
|
|
2506
2701
|
await page.keyboard.press("Backspace");
|
|
2507
2702
|
logger6.success("humanClear");
|
|
2508
2703
|
} catch (error) {
|
|
@@ -2528,13 +2723,13 @@ var Humanize = {
|
|
|
2528
2723
|
const x = 100 + Math.random() * (viewportSize.width - 200);
|
|
2529
2724
|
const y = 100 + Math.random() * (viewportSize.height - 200);
|
|
2530
2725
|
await cursor.actions.move({ x, y });
|
|
2531
|
-
await
|
|
2726
|
+
await delay2(this.jitterMs(350, 0.4));
|
|
2532
2727
|
} else if (action < 0.7) {
|
|
2533
2728
|
const scrollY = (Math.random() - 0.5) * 200;
|
|
2534
2729
|
await page.mouse.wheel(0, scrollY);
|
|
2535
|
-
await
|
|
2730
|
+
await delay2(this.jitterMs(500, 0.4));
|
|
2536
2731
|
} else {
|
|
2537
|
-
await
|
|
2732
|
+
await delay2(this.jitterMs(800, 0.5));
|
|
2538
2733
|
}
|
|
2539
2734
|
}
|
|
2540
2735
|
logger6.success("warmUpBrowsing");
|
|
@@ -2563,7 +2758,7 @@ var Humanize = {
|
|
|
2563
2758
|
const scrollAmount = stepDistance * factor * sign * jitter;
|
|
2564
2759
|
await page.mouse.wheel(0, scrollAmount);
|
|
2565
2760
|
const baseDelay = 60 + i * 25;
|
|
2566
|
-
await
|
|
2761
|
+
await delay2(this.jitterMs(baseDelay, 0.3));
|
|
2567
2762
|
}
|
|
2568
2763
|
logger6.success("naturalScroll");
|
|
2569
2764
|
} catch (error) {
|
|
@@ -2573,8 +2768,143 @@ var Humanize = {
|
|
|
2573
2768
|
}
|
|
2574
2769
|
};
|
|
2575
2770
|
|
|
2771
|
+
// src/internals/humanize/machine.js
|
|
2772
|
+
var resolveDeviceFromPage = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
2773
|
+
var isPoint = (value) => value && typeof value === "object" && Number.isFinite(Number(value.x)) && Number.isFinite(Number(value.y));
|
|
2774
|
+
var resolveNativeTarget = (page, target) => {
|
|
2775
|
+
if (!target) return null;
|
|
2776
|
+
if (typeof target === "string") {
|
|
2777
|
+
return page.locator(target);
|
|
2778
|
+
}
|
|
2779
|
+
return target;
|
|
2780
|
+
};
|
|
2781
|
+
var normalizeSelectorOptions = (options = {}) => ({
|
|
2782
|
+
targetParent: Boolean(options.targetParent ?? options.parent ?? false),
|
|
2783
|
+
pick: options.pick === "last" || options.last ? "last" : "first",
|
|
2784
|
+
visibleOnly: options.visibleOnly !== false,
|
|
2785
|
+
fallbackDomClick: Boolean(options.fallbackDomClick),
|
|
2786
|
+
forceMouse: Boolean(options.forceMouse)
|
|
2787
|
+
});
|
|
2788
|
+
var MachineHumanize = {
|
|
2789
|
+
async clickPoint(page, pointOrX, maybeY, options = {}) {
|
|
2790
|
+
const clickOptions = isPoint(pointOrX) && maybeY && typeof maybeY === "object" ? maybeY : options;
|
|
2791
|
+
const point = isPoint(pointOrX) ? pointOrX : { x: pointOrX, y: maybeY };
|
|
2792
|
+
const x = Number(point.x);
|
|
2793
|
+
const y = Number(point.y);
|
|
2794
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
2795
|
+
throw new Error(`Invalid click point: ${JSON.stringify(point)}`);
|
|
2796
|
+
}
|
|
2797
|
+
const resolvedDevice = resolveDeviceFromPage(page);
|
|
2798
|
+
if (resolvedDevice === Device.Mobile && !clickOptions.forceMouse && page.touchscreen && typeof page.touchscreen.tap === "function") {
|
|
2799
|
+
await page.touchscreen.tap(x, y);
|
|
2800
|
+
return true;
|
|
2801
|
+
}
|
|
2802
|
+
await page.mouse.click(x, y, clickOptions.mouseOptions || {});
|
|
2803
|
+
return true;
|
|
2804
|
+
},
|
|
2805
|
+
async click(page, target, options = {}) {
|
|
2806
|
+
if (isPoint(target)) {
|
|
2807
|
+
return MachineHumanize.clickPoint(page, target, void 0, options);
|
|
2808
|
+
}
|
|
2809
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2810
|
+
if (!nativeTarget || typeof nativeTarget.click !== "function") {
|
|
2811
|
+
throw new Error("Machine click target does not expose click()");
|
|
2812
|
+
}
|
|
2813
|
+
await nativeTarget.click(options.clickOptions || options);
|
|
2814
|
+
return true;
|
|
2815
|
+
},
|
|
2816
|
+
async focus(page, target, options = {}) {
|
|
2817
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2818
|
+
if (!nativeTarget || typeof nativeTarget.focus !== "function") {
|
|
2819
|
+
throw new Error("Machine focus target does not expose focus()");
|
|
2820
|
+
}
|
|
2821
|
+
await nativeTarget.focus(options);
|
|
2822
|
+
return true;
|
|
2823
|
+
},
|
|
2824
|
+
async fill(page, target, value, options = {}) {
|
|
2825
|
+
const nativeTarget = resolveNativeTarget(page, target);
|
|
2826
|
+
if (!nativeTarget || typeof nativeTarget.fill !== "function") {
|
|
2827
|
+
throw new Error("Machine fill target does not expose fill()");
|
|
2828
|
+
}
|
|
2829
|
+
await nativeTarget.fill(value, options);
|
|
2830
|
+
return true;
|
|
2831
|
+
},
|
|
2832
|
+
async keyboardType(page, text, options = {}) {
|
|
2833
|
+
await page.keyboard.type(text, options);
|
|
2834
|
+
return true;
|
|
2835
|
+
},
|
|
2836
|
+
async type(page, targetOrText, maybeText, options = {}) {
|
|
2837
|
+
if (maybeText == null || typeof maybeText === "object" && !Array.isArray(maybeText)) {
|
|
2838
|
+
return MachineHumanize.keyboardType(page, targetOrText, maybeText || options);
|
|
2839
|
+
}
|
|
2840
|
+
const nativeTarget = resolveNativeTarget(page, targetOrText);
|
|
2841
|
+
if (!nativeTarget || typeof nativeTarget.type !== "function") {
|
|
2842
|
+
throw new Error("Machine type target does not expose type()");
|
|
2843
|
+
}
|
|
2844
|
+
await nativeTarget.type(maybeText, options);
|
|
2845
|
+
return true;
|
|
2846
|
+
},
|
|
2847
|
+
async selectorCenterPoint(page, selector, options = {}) {
|
|
2848
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2849
|
+
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2850
|
+
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2851
|
+
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2852
|
+
let rect = null;
|
|
2853
|
+
for (const node of ordered) {
|
|
2854
|
+
if (!node) continue;
|
|
2855
|
+
const target = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2856
|
+
rect = target.getBoundingClientRect?.() || null;
|
|
2857
|
+
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2858
|
+
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2859
|
+
break;
|
|
2860
|
+
}
|
|
2861
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) return null;
|
|
2862
|
+
return {
|
|
2863
|
+
x: rect.left + rect.width / 2,
|
|
2864
|
+
y: rect.top + rect.height / 2
|
|
2865
|
+
};
|
|
2866
|
+
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2867
|
+
},
|
|
2868
|
+
async domClick(page, selector, options = {}) {
|
|
2869
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2870
|
+
return page.evaluate(({ innerSelector, innerOptions }) => {
|
|
2871
|
+
const nodes = Array.from(document.querySelectorAll(innerSelector));
|
|
2872
|
+
const ordered = innerOptions.pick === "last" ? nodes.reverse() : nodes;
|
|
2873
|
+
let target = null;
|
|
2874
|
+
for (const node of ordered) {
|
|
2875
|
+
if (!node) continue;
|
|
2876
|
+
const candidate = innerOptions.targetParent ? node.parentElement || node : node;
|
|
2877
|
+
const rect = candidate.getBoundingClientRect?.() || null;
|
|
2878
|
+
const isVisible = Boolean(rect && rect.width > 0 && rect.height > 0);
|
|
2879
|
+
if (innerOptions.visibleOnly && !isVisible) continue;
|
|
2880
|
+
target = candidate;
|
|
2881
|
+
break;
|
|
2882
|
+
}
|
|
2883
|
+
if (!target || typeof target.click !== "function") return false;
|
|
2884
|
+
target.click();
|
|
2885
|
+
return true;
|
|
2886
|
+
}, { innerSelector: selector, innerOptions: normalizedOptions });
|
|
2887
|
+
},
|
|
2888
|
+
async clickSelectorCenter(page, selector, options = {}) {
|
|
2889
|
+
const normalizedOptions = normalizeSelectorOptions(options);
|
|
2890
|
+
try {
|
|
2891
|
+
const point = await MachineHumanize.selectorCenterPoint(page, selector, normalizedOptions);
|
|
2892
|
+
if (point) {
|
|
2893
|
+
await MachineHumanize.clickPoint(page, point, void 0, normalizedOptions);
|
|
2894
|
+
return true;
|
|
2895
|
+
}
|
|
2896
|
+
} catch (error) {
|
|
2897
|
+
if (!normalizedOptions.fallbackDomClick) throw error;
|
|
2898
|
+
}
|
|
2899
|
+
if (normalizedOptions.fallbackDomClick) {
|
|
2900
|
+
return MachineHumanize.domClick(page, selector, normalizedOptions);
|
|
2901
|
+
}
|
|
2902
|
+
return false;
|
|
2903
|
+
}
|
|
2904
|
+
};
|
|
2905
|
+
|
|
2576
2906
|
// src/internals/humanize/shared.js
|
|
2577
|
-
import
|
|
2907
|
+
import delay3 from "delay";
|
|
2578
2908
|
var jitterMs = (base, jitterPercent = 0.3) => {
|
|
2579
2909
|
const jitter = Number(base || 0) * Number(jitterPercent || 0) * (Math.random() * 2 - 1);
|
|
2580
2910
|
return Math.max(10, Math.round(Number(base || 0) + jitter));
|
|
@@ -2600,7 +2930,7 @@ var resolveElement = async (page, target, { throwOnMissing = true } = {}) => {
|
|
|
2600
2930
|
}
|
|
2601
2931
|
return element;
|
|
2602
2932
|
};
|
|
2603
|
-
var waitJitter = (base, jitterPercent = 0.3) =>
|
|
2933
|
+
var waitJitter = (base, jitterPercent = 0.3) => delay3(jitterMs(base, jitterPercent));
|
|
2604
2934
|
|
|
2605
2935
|
// src/internals/humanize/mobile.js
|
|
2606
2936
|
var logger7 = createInternalLogger("Humanize.Mobile");
|
|
@@ -2902,6 +3232,49 @@ var scrollScrollableAncestor = async (element, deltaY) => {
|
|
|
2902
3232
|
};
|
|
2903
3233
|
}, deltaY);
|
|
2904
3234
|
};
|
|
3235
|
+
var scrollAwayFromObstruction = async (element, status) => {
|
|
3236
|
+
return element.evaluate((el, innerStatus) => {
|
|
3237
|
+
const isScrollable = (node) => {
|
|
3238
|
+
const style = window.getComputedStyle(node);
|
|
3239
|
+
if (!style) return false;
|
|
3240
|
+
const overflowY = style.overflowY;
|
|
3241
|
+
if (!["auto", "scroll", "overlay"].includes(overflowY)) return false;
|
|
3242
|
+
return node.scrollHeight > node.clientHeight + 1;
|
|
3243
|
+
};
|
|
3244
|
+
let scroller = el;
|
|
3245
|
+
while (scroller && scroller !== document.body) {
|
|
3246
|
+
if (isScrollable(scroller)) break;
|
|
3247
|
+
scroller = scroller.parentElement;
|
|
3248
|
+
}
|
|
3249
|
+
if (!scroller || scroller === document.body) {
|
|
3250
|
+
return { moved: false, scrollTop: 0, deltaY: 0 };
|
|
3251
|
+
}
|
|
3252
|
+
const rect = el.getBoundingClientRect();
|
|
3253
|
+
const scrollerRect = scroller.getBoundingClientRect();
|
|
3254
|
+
const obstruction = innerStatus?.obstruction || {};
|
|
3255
|
+
const obstructionTop = Number(obstruction.top);
|
|
3256
|
+
const obstructionBottom = Number(obstruction.bottom);
|
|
3257
|
+
const obstructionMiddle = Number.isFinite(obstructionTop) && Number.isFinite(obstructionBottom) ? (obstructionTop + obstructionBottom) / 2 : scrollerRect.top + scrollerRect.height / 2;
|
|
3258
|
+
const scrollerMiddle = scrollerRect.top + scrollerRect.height / 2;
|
|
3259
|
+
const padding = 18;
|
|
3260
|
+
let deltaY = 0;
|
|
3261
|
+
if (Number.isFinite(obstructionTop) && rect.bottom > obstructionTop && obstructionTop >= scrollerRect.top && obstructionTop <= scrollerRect.bottom && obstructionMiddle >= scrollerMiddle) {
|
|
3262
|
+
deltaY = rect.bottom - obstructionTop + padding;
|
|
3263
|
+
} else if (Number.isFinite(obstructionBottom) && rect.top < obstructionBottom && obstructionBottom >= scrollerRect.top && obstructionBottom <= scrollerRect.bottom && obstructionMiddle < scrollerMiddle) {
|
|
3264
|
+
deltaY = rect.top - obstructionBottom - padding;
|
|
3265
|
+
} else {
|
|
3266
|
+
const fallbackDistance = Math.max(48, Math.min(180, scrollerRect.height * 0.45));
|
|
3267
|
+
deltaY = obstructionMiddle >= scrollerMiddle ? fallbackDistance : -fallbackDistance;
|
|
3268
|
+
}
|
|
3269
|
+
const beforeTop = scroller.scrollTop;
|
|
3270
|
+
scroller.scrollTop = beforeTop + deltaY;
|
|
3271
|
+
return {
|
|
3272
|
+
moved: Math.abs(scroller.scrollTop - beforeTop) > 2,
|
|
3273
|
+
scrollTop: scroller.scrollTop,
|
|
3274
|
+
deltaY
|
|
3275
|
+
};
|
|
3276
|
+
}, status);
|
|
3277
|
+
};
|
|
2905
3278
|
var dispatchTouchSwipe = async (page, deltaY, options = {}) => {
|
|
2906
3279
|
const viewport = resolveViewport(page);
|
|
2907
3280
|
const rawRect = options.rect || null;
|
|
@@ -3065,6 +3438,15 @@ var MobileHumanize = {
|
|
|
3065
3438
|
logger7.warn(`humanScroll | fixed/sticky \u76EE\u6807\u88AB\u906E\u6321\uFF0C\u6EDA\u52A8\u65E0\u6CD5\u89E3\u9664 (${status.obstruction?.tag || "unknown"})`);
|
|
3066
3439
|
return { element, didScroll, restore: null };
|
|
3067
3440
|
}
|
|
3441
|
+
if (scrollRect && status.code === "OBSTRUCTED" && status.obstruction?.isFixed) {
|
|
3442
|
+
const moved = await scrollAwayFromObstruction(element, status);
|
|
3443
|
+
if (moved.moved) {
|
|
3444
|
+
logger7.debug(`humanScroll | sticky/fixed \u906E\u6321\u8865\u507F\u6EDA\u52A8 top=${Math.round(moved.scrollTop || 0)}`);
|
|
3445
|
+
await waitJitter(90, 0.3);
|
|
3446
|
+
didScroll = true;
|
|
3447
|
+
continue;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3068
3450
|
if (Date.now() - startTime > maxDurationMs) {
|
|
3069
3451
|
logger7.warn(`humanScroll | mobile timeout (${maxDurationMs}ms, status=${status.code}, direction=${status.direction || "unknown"}, fixed=${Boolean(status.isFixed)})`);
|
|
3070
3452
|
return { element, didScroll, restore: null };
|
|
@@ -3196,7 +3578,7 @@ var MobileHumanize = {
|
|
|
3196
3578
|
await MobileHumanize.humanScroll(page, element, { throwOnMissing });
|
|
3197
3579
|
}
|
|
3198
3580
|
const status = await checkElementVisibility(element).catch(() => null);
|
|
3199
|
-
if (status &&
|
|
3581
|
+
if (status && status.code !== "VISIBLE") {
|
|
3200
3582
|
const message = `\u5143\u7D20\u4E0D\u53EF\u70B9\u51FB: ${status.reason || status.code}`;
|
|
3201
3583
|
if (throwOnMissing) throw new Error(message);
|
|
3202
3584
|
logger7.warn(`humanClick: ${message}\uFF0C\u8DF3\u8FC7\u70B9\u51FB`);
|
|
@@ -3256,6 +3638,40 @@ var MobileHumanize = {
|
|
|
3256
3638
|
}
|
|
3257
3639
|
}
|
|
3258
3640
|
},
|
|
3641
|
+
async humanPress(page, targetOrKey, maybeKey, options = {}) {
|
|
3642
|
+
const hasTarget = typeof maybeKey === "string";
|
|
3643
|
+
const key = hasTarget ? maybeKey : targetOrKey;
|
|
3644
|
+
const pressOptions = hasTarget ? options : maybeKey || options;
|
|
3645
|
+
const {
|
|
3646
|
+
reactionDelay = 170,
|
|
3647
|
+
holdDelay = 42,
|
|
3648
|
+
focusDelay = 180,
|
|
3649
|
+
scrollIfNeeded = true,
|
|
3650
|
+
throwOnMissing = true,
|
|
3651
|
+
keyboardOptions = {}
|
|
3652
|
+
} = pressOptions || {};
|
|
3653
|
+
const targetDesc = hasTarget ? describeTarget(targetOrKey) : "current focus";
|
|
3654
|
+
logger7.start("humanPress", `key=${key}, target=${targetDesc}`);
|
|
3655
|
+
try {
|
|
3656
|
+
if (hasTarget) {
|
|
3657
|
+
await MobileHumanize.humanClick(page, targetOrKey, {
|
|
3658
|
+
reactionDelay: focusDelay,
|
|
3659
|
+
scrollIfNeeded,
|
|
3660
|
+
throwOnMissing
|
|
3661
|
+
});
|
|
3662
|
+
}
|
|
3663
|
+
await waitJitter(reactionDelay, 0.45);
|
|
3664
|
+
await page.keyboard.press(key, {
|
|
3665
|
+
...keyboardOptions,
|
|
3666
|
+
delay: jitterMs(holdDelay, 0.5)
|
|
3667
|
+
});
|
|
3668
|
+
logger7.success("humanPress");
|
|
3669
|
+
return true;
|
|
3670
|
+
} catch (error) {
|
|
3671
|
+
logger7.fail("humanPress", error);
|
|
3672
|
+
throw error;
|
|
3673
|
+
}
|
|
3674
|
+
},
|
|
3259
3675
|
async humanClear(page, selector) {
|
|
3260
3676
|
const locator = page.locator(selector);
|
|
3261
3677
|
await MobileHumanize.humanClick(page, locator, { scrollIfNeeded: true });
|
|
@@ -3306,15 +3722,18 @@ var MobileHumanize = {
|
|
|
3306
3722
|
};
|
|
3307
3723
|
|
|
3308
3724
|
// src/humanize.js
|
|
3309
|
-
var
|
|
3725
|
+
var resolveDeviceFromPage2 = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
3310
3726
|
var resolveDelegate = (page) => {
|
|
3311
|
-
return
|
|
3727
|
+
return resolveDeviceFromPage2(page) === Device.Mobile ? MobileHumanize : Humanize;
|
|
3312
3728
|
};
|
|
3313
3729
|
var callDelegate = (method, page, args) => {
|
|
3314
3730
|
const delegate = resolveDelegate(page);
|
|
3315
3731
|
return delegate[method](page, ...args);
|
|
3316
3732
|
};
|
|
3317
3733
|
var Humanize2 = {
|
|
3734
|
+
// M = Machine: native/mechanical operations for兼容路径。
|
|
3735
|
+
// 这组 API 不做人类化处理,只保留原生 click / focus / fill / type 等语义。
|
|
3736
|
+
M: MachineHumanize,
|
|
3318
3737
|
jitterMs(base, jitterPercent = 0.3) {
|
|
3319
3738
|
return Humanize.jitterMs(base, jitterPercent);
|
|
3320
3739
|
},
|
|
@@ -3343,6 +3762,12 @@ var Humanize2 = {
|
|
|
3343
3762
|
humanType(page, selector, text, options = {}) {
|
|
3344
3763
|
return callDelegate("humanType", page, [selector, text, options]);
|
|
3345
3764
|
},
|
|
3765
|
+
humanPress(page, targetOrKey, maybeKey, options = {}) {
|
|
3766
|
+
if (typeof maybeKey === "string") {
|
|
3767
|
+
return callDelegate("humanPress", page, [targetOrKey, maybeKey, options]);
|
|
3768
|
+
}
|
|
3769
|
+
return callDelegate("humanPress", page, [targetOrKey, maybeKey || options]);
|
|
3770
|
+
},
|
|
3346
3771
|
humanClear(page, selector) {
|
|
3347
3772
|
return callDelegate("humanClear", page, [selector]);
|
|
3348
3773
|
},
|
|
@@ -5814,7 +6239,7 @@ var Logger = {
|
|
|
5814
6239
|
};
|
|
5815
6240
|
|
|
5816
6241
|
// src/share.js
|
|
5817
|
-
import
|
|
6242
|
+
import delay4 from "delay";
|
|
5818
6243
|
|
|
5819
6244
|
// src/internals/watermarkify.js
|
|
5820
6245
|
var DEFAULT_TIMEZONE_OFFSET = 8;
|
|
@@ -5848,6 +6273,18 @@ var DEFAULT_STRIP_PROMPT_MAX_LINES = 3;
|
|
|
5848
6273
|
var DEFAULT_STRIP_CONTENT_SAFE_PADDING_X = 12;
|
|
5849
6274
|
var DEFAULT_STRIP_TEXT_WIDTH_SAFETY = 10;
|
|
5850
6275
|
var DEFAULT_BROWSER_COMPOSITOR_VIEWPORT_HEIGHT = 1200;
|
|
6276
|
+
var MOBILE_STRIP_HEIGHT = 104;
|
|
6277
|
+
var MOBILE_STRIP_PADDING_X = 14;
|
|
6278
|
+
var MOBILE_STRIP_PADDING_Y = 13;
|
|
6279
|
+
var MOBILE_STRIP_ROW_HEIGHT = 22;
|
|
6280
|
+
var MOBILE_STRIP_ROW_GAP = 7;
|
|
6281
|
+
var MOBILE_STRIP_LABEL_FONT_SIZE = 12.5;
|
|
6282
|
+
var MOBILE_STRIP_VALUE_FONT_SIZE = 13.5;
|
|
6283
|
+
var MOBILE_STRIP_LABEL_WIDTH = 45;
|
|
6284
|
+
var MOBILE_STRIP_PROMPT_LABEL_WIDTH = 60;
|
|
6285
|
+
var MOBILE_STRIP_LOC_LABEL_WIDTH = 30;
|
|
6286
|
+
var MOBILE_STRIP_IP_LABEL_WIDTH = 22;
|
|
6287
|
+
var MOBILE_STRIP_META_GAP = 12;
|
|
5851
6288
|
var WEAK_LOCATION_VALUES = /* @__PURE__ */ new Set(["cn", "\u4E2D\u56FD"]);
|
|
5852
6289
|
var LOCATION_NETWORK_SUFFIX_PATTERNS = [
|
|
5853
6290
|
/(?:中国)?移动$/i,
|
|
@@ -6632,10 +7069,16 @@ var resolveScreenshotWatermarkifyMeta = async (page, options = {}) => {
|
|
|
6632
7069
|
}),
|
|
6633
7070
|
watermark: options.watermark,
|
|
6634
7071
|
strip: options.strip,
|
|
6635
|
-
stripLogoSrc
|
|
7072
|
+
stripLogoSrc,
|
|
7073
|
+
device: resolvePageDevice(page)
|
|
6636
7074
|
};
|
|
6637
7075
|
};
|
|
6638
7076
|
var buildFontFamily = () => 'MiSans, "SF Pro Display", "PingFang SC", "Helvetica Neue", Arial, sans-serif';
|
|
7077
|
+
var resolvePageDevice = (page) => normalizeDevice(page?.[PageRuntimeStateKey]?.device);
|
|
7078
|
+
var findStripSegment = (segments, kind) => {
|
|
7079
|
+
const found = Array.isArray(segments) ? segments.find((segment) => segment?.kind === kind) : null;
|
|
7080
|
+
return found && typeof found === "object" ? found : {};
|
|
7081
|
+
};
|
|
6639
7082
|
var buildStripSegmentLayout = (segment, options = {}) => {
|
|
6640
7083
|
const label = normalizeWhitespace(segment?.label || "");
|
|
6641
7084
|
const rawValue = normalizeWhitespace(segment?.rawValue || segment?.value || "-") || "-";
|
|
@@ -6963,6 +7406,116 @@ var renderStripDivider = (x, centerY, height) => {
|
|
|
6963
7406
|
/>
|
|
6964
7407
|
`;
|
|
6965
7408
|
};
|
|
7409
|
+
var renderMobileStripText = ({
|
|
7410
|
+
label,
|
|
7411
|
+
value,
|
|
7412
|
+
labelX,
|
|
7413
|
+
y,
|
|
7414
|
+
valueWidth,
|
|
7415
|
+
labelWidth = MOBILE_STRIP_LABEL_WIDTH,
|
|
7416
|
+
fontFamily
|
|
7417
|
+
}) => {
|
|
7418
|
+
const safeLabel = normalizeWhitespace(label) || "-";
|
|
7419
|
+
const safeValue = fitTextWithEllipsis(
|
|
7420
|
+
Array.from(normalizeWhitespace(value) || "-"),
|
|
7421
|
+
Math.max(18, valueWidth),
|
|
7422
|
+
MOBILE_STRIP_VALUE_FONT_SIZE
|
|
7423
|
+
);
|
|
7424
|
+
return `
|
|
7425
|
+
<text
|
|
7426
|
+
x="${labelX}"
|
|
7427
|
+
y="${y}"
|
|
7428
|
+
fill="#111111"
|
|
7429
|
+
fill-opacity="0.54"
|
|
7430
|
+
font-family="${fontFamily}"
|
|
7431
|
+
font-size="${MOBILE_STRIP_LABEL_FONT_SIZE}"
|
|
7432
|
+
font-weight="650"
|
|
7433
|
+
dominant-baseline="middle"
|
|
7434
|
+
>${escapeXml(safeLabel)}</text>
|
|
7435
|
+
<text
|
|
7436
|
+
x="${labelX + labelWidth}"
|
|
7437
|
+
y="${y}"
|
|
7438
|
+
fill="#111111"
|
|
7439
|
+
font-family="${fontFamily}"
|
|
7440
|
+
font-size="${MOBILE_STRIP_VALUE_FONT_SIZE}"
|
|
7441
|
+
font-weight="730"
|
|
7442
|
+
dominant-baseline="middle"
|
|
7443
|
+
>${escapeXml(safeValue)}</text>
|
|
7444
|
+
`;
|
|
7445
|
+
};
|
|
7446
|
+
var renderMobileStrip = ({ meta, width, height, fontFamily }) => {
|
|
7447
|
+
const stripHeight = MOBILE_STRIP_HEIGHT;
|
|
7448
|
+
const stripY = height - stripHeight;
|
|
7449
|
+
const contentX = MOBILE_STRIP_PADDING_X;
|
|
7450
|
+
const contentWidth = Math.max(1, width - MOBILE_STRIP_PADDING_X * 2);
|
|
7451
|
+
const prompt = findStripSegment(meta.stripSegments, "prompt");
|
|
7452
|
+
const time = findStripSegment(meta.stripSegments, "time");
|
|
7453
|
+
const location = findStripSegment(meta.stripSegments, "location");
|
|
7454
|
+
const ip = findStripSegment(meta.stripSegments, "ip");
|
|
7455
|
+
const row1Y = stripY + MOBILE_STRIP_PADDING_Y + MOBILE_STRIP_ROW_HEIGHT / 2;
|
|
7456
|
+
const row2Y = row1Y + MOBILE_STRIP_ROW_HEIGHT + MOBILE_STRIP_ROW_GAP;
|
|
7457
|
+
const row3Y = row2Y + MOBILE_STRIP_ROW_HEIGHT + MOBILE_STRIP_ROW_GAP;
|
|
7458
|
+
const promptValueWidth = contentWidth - MOBILE_STRIP_PROMPT_LABEL_WIDTH;
|
|
7459
|
+
const locValueX = contentX + MOBILE_STRIP_LOC_LABEL_WIDTH;
|
|
7460
|
+
const ipLabelX = Math.min(
|
|
7461
|
+
width - MOBILE_STRIP_PADDING_X - MOBILE_STRIP_IP_LABEL_WIDTH - 70,
|
|
7462
|
+
Math.max(
|
|
7463
|
+
locValueX + 80 + MOBILE_STRIP_META_GAP,
|
|
7464
|
+
contentX + Math.round(contentWidth * 0.52)
|
|
7465
|
+
)
|
|
7466
|
+
);
|
|
7467
|
+
const locValueWidth = Math.max(
|
|
7468
|
+
28,
|
|
7469
|
+
ipLabelX - locValueX - MOBILE_STRIP_META_GAP
|
|
7470
|
+
);
|
|
7471
|
+
const ipValueWidth = Math.max(
|
|
7472
|
+
28,
|
|
7473
|
+
width - MOBILE_STRIP_PADDING_X - (ipLabelX + MOBILE_STRIP_IP_LABEL_WIDTH)
|
|
7474
|
+
);
|
|
7475
|
+
return `
|
|
7476
|
+
<g id="strip">
|
|
7477
|
+
<rect x="0" y="${stripY}" width="${width}" height="${stripHeight}" fill="#ffffff" fill-opacity="0.985" />
|
|
7478
|
+
<rect x="0" y="${stripY}" width="${width}" height="1" fill="#111111" fill-opacity="0.10" />
|
|
7479
|
+
<rect x="${contentX}" y="${stripY}" width="${Math.max(96, Math.round(width * 0.36))}" height="2" fill="url(#stripAccent)" />
|
|
7480
|
+
${renderMobileStripText({
|
|
7481
|
+
label: "Prompt",
|
|
7482
|
+
value: prompt.rawValue || prompt.value || "-",
|
|
7483
|
+
labelX: contentX,
|
|
7484
|
+
y: row1Y,
|
|
7485
|
+
valueWidth: promptValueWidth,
|
|
7486
|
+
labelWidth: MOBILE_STRIP_PROMPT_LABEL_WIDTH,
|
|
7487
|
+
fontFamily
|
|
7488
|
+
})}
|
|
7489
|
+
${renderMobileStripText({
|
|
7490
|
+
label: "Time",
|
|
7491
|
+
value: time.rawValue || time.value || "-",
|
|
7492
|
+
labelX: contentX,
|
|
7493
|
+
y: row2Y,
|
|
7494
|
+
valueWidth: promptValueWidth,
|
|
7495
|
+
labelWidth: MOBILE_STRIP_LABEL_WIDTH,
|
|
7496
|
+
fontFamily
|
|
7497
|
+
})}
|
|
7498
|
+
${renderMobileStripText({
|
|
7499
|
+
label: "Loc",
|
|
7500
|
+
value: location.rawValue || location.value || "-",
|
|
7501
|
+
labelX: contentX,
|
|
7502
|
+
y: row3Y,
|
|
7503
|
+
valueWidth: locValueWidth,
|
|
7504
|
+
labelWidth: MOBILE_STRIP_LOC_LABEL_WIDTH,
|
|
7505
|
+
fontFamily
|
|
7506
|
+
})}
|
|
7507
|
+
${renderMobileStripText({
|
|
7508
|
+
label: "IP",
|
|
7509
|
+
value: ip.rawValue || ip.value || "-",
|
|
7510
|
+
labelX: ipLabelX,
|
|
7511
|
+
y: row3Y,
|
|
7512
|
+
valueWidth: ipValueWidth,
|
|
7513
|
+
labelWidth: MOBILE_STRIP_IP_LABEL_WIDTH,
|
|
7514
|
+
fontFamily
|
|
7515
|
+
})}
|
|
7516
|
+
</g>
|
|
7517
|
+
`;
|
|
7518
|
+
};
|
|
6966
7519
|
var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
|
|
6967
7520
|
const hasWatermark = meta?.watermark?.enabled !== false && normalizeText(meta?.watermarkText);
|
|
6968
7521
|
const hasStrip = meta?.strip?.enabled !== false && Array.isArray(meta?.stripSegments) && meta.stripSegments.length > 0;
|
|
@@ -7011,6 +7564,27 @@ var buildWatermarkifySvg = (meta, imageWidth, imageHeight) => {
|
|
|
7011
7564
|
parts.push(`<g id="watermarks">${stamps.join("")}</g>`);
|
|
7012
7565
|
}
|
|
7013
7566
|
if (hasStrip) {
|
|
7567
|
+
if (normalizeDevice(meta.device) === Device.Mobile) {
|
|
7568
|
+
parts.push(renderMobileStrip({
|
|
7569
|
+
meta,
|
|
7570
|
+
width,
|
|
7571
|
+
height,
|
|
7572
|
+
fontFamily
|
|
7573
|
+
}));
|
|
7574
|
+
return `
|
|
7575
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
7576
|
+
<defs>
|
|
7577
|
+
<linearGradient id="stripAccent" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
7578
|
+
<stop offset="0%" stop-color="#111111" stop-opacity="0.98" />
|
|
7579
|
+
<stop offset="24%" stop-color="#111111" stop-opacity="0.68" />
|
|
7580
|
+
<stop offset="58%" stop-color="#111111" stop-opacity="0.08" />
|
|
7581
|
+
<stop offset="100%" stop-color="#111111" stop-opacity="0" />
|
|
7582
|
+
</linearGradient>
|
|
7583
|
+
</defs>
|
|
7584
|
+
${parts.join("")}
|
|
7585
|
+
</svg>
|
|
7586
|
+
`;
|
|
7587
|
+
}
|
|
7014
7588
|
const logoSize = 28;
|
|
7015
7589
|
const logoX = DEFAULT_STRIP_PADDING_LEFT;
|
|
7016
7590
|
const contentStartX = logoX + logoSize + DEFAULT_STRIP_GAP;
|
|
@@ -7137,16 +7711,16 @@ var watermarkifyScreenshotBuffer = async (buffer, meta, page = null) => {
|
|
|
7137
7711
|
return await composeScreenshotBufferWithBrowser(page, buffer, overlaySvg, imageInfo);
|
|
7138
7712
|
};
|
|
7139
7713
|
|
|
7140
|
-
// src/internals/
|
|
7714
|
+
// src/internals/compression.js
|
|
7141
7715
|
import { Jimp, JimpMime, ResizeStrategy } from "jimp";
|
|
7142
|
-
var logger14 = createInternalLogger("
|
|
7716
|
+
var logger14 = createInternalLogger("Compression");
|
|
7143
7717
|
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
7144
7718
|
var DEFAULT_SCREENSHOT_OUTPUT_TYPE = "jpeg";
|
|
7145
7719
|
var DEFAULT_SCREENSHOT_QUALITY = 0.72;
|
|
7146
7720
|
var DEFAULT_SCREENSHOT_MIN_QUALITY = 0.38;
|
|
7147
7721
|
var DEFAULT_SCREENSHOT_MIN_SCALE = 0.25;
|
|
7148
7722
|
var SUPPORTED_SCREENSHOT_OUTPUT_TYPES = /* @__PURE__ */ new Set(["jpeg"]);
|
|
7149
|
-
var
|
|
7723
|
+
var toPositiveInteger2 = (value, fallback = 0) => {
|
|
7150
7724
|
const number = Math.floor(Number(value) || 0);
|
|
7151
7725
|
return number > 0 ? number : fallback;
|
|
7152
7726
|
};
|
|
@@ -7168,7 +7742,7 @@ var normalizeScreenshotOutputType = (value) => {
|
|
|
7168
7742
|
};
|
|
7169
7743
|
var getBase64BytesFromBuffer = (buffer) => Math.ceil(buffer.length / 3) * 4;
|
|
7170
7744
|
var toJpegQuality = (value) => Math.round(normalizeQuality2(value, DEFAULT_SCREENSHOT_QUALITY) * 100);
|
|
7171
|
-
var
|
|
7745
|
+
var resolveImageCompression = (options = {}) => {
|
|
7172
7746
|
const explicit = options.compression;
|
|
7173
7747
|
const source = explicit && typeof explicit === "object" && !Array.isArray(explicit) ? explicit : {};
|
|
7174
7748
|
const enabled = explicit !== false && source.enabled !== false && options.compress !== false;
|
|
@@ -7182,7 +7756,7 @@ var resolveCaptureScreenCompression = (options = {}) => {
|
|
|
7182
7756
|
);
|
|
7183
7757
|
return {
|
|
7184
7758
|
enabled,
|
|
7185
|
-
maxBytes:
|
|
7759
|
+
maxBytes: toPositiveInteger2(
|
|
7186
7760
|
source.maxBytes ?? source.maxBase64Bytes ?? options.maxBytes ?? options.maxBase64Bytes,
|
|
7187
7761
|
DEFAULT_SCREENSHOT_MAX_BYTES
|
|
7188
7762
|
),
|
|
@@ -7219,7 +7793,7 @@ var encodeJpeg = async (sourceImage, compression, scale, quality) => {
|
|
|
7219
7793
|
format: compression.outputType
|
|
7220
7794
|
};
|
|
7221
7795
|
};
|
|
7222
|
-
var
|
|
7796
|
+
var compressImageBuffer = async (buffer, compression) => {
|
|
7223
7797
|
const sourceImage = await Jimp.read(buffer);
|
|
7224
7798
|
const maxQuality = toJpegQuality(compression.quality);
|
|
7225
7799
|
const minQuality = Math.min(maxQuality, toJpegQuality(compression.minQuality));
|
|
@@ -7252,12 +7826,12 @@ var compressScreenshotBuffer = async (buffer, compression) => {
|
|
|
7252
7826
|
const fallback = !smallest || finalCandidate.bytes < smallest.bytes ? finalCandidate : smallest;
|
|
7253
7827
|
return { ...fallback, withinLimit: fallback.bytes <= compression.maxBytes };
|
|
7254
7828
|
};
|
|
7255
|
-
var
|
|
7829
|
+
var compressImageBufferToBase64 = async (buffer, compression) => {
|
|
7256
7830
|
const originalBytes = getBase64BytesFromBuffer(buffer);
|
|
7257
7831
|
if (!compression.enabled || originalBytes <= compression.maxBytes) {
|
|
7258
7832
|
return buffer.toString("base64");
|
|
7259
7833
|
}
|
|
7260
|
-
const result = await
|
|
7834
|
+
const result = await compressImageBuffer(buffer, compression).catch((error) => {
|
|
7261
7835
|
logger14.warning(`captureScreen \u538B\u7F29\u5931\u8D25\uFF0C\u8FD4\u56DE\u539F\u56FE: ${error instanceof Error ? error.message : String(error)}`);
|
|
7262
7836
|
return null;
|
|
7263
7837
|
});
|
|
@@ -7656,7 +8230,7 @@ var Share = {
|
|
|
7656
8230
|
);
|
|
7657
8231
|
nextProgressLogTs = now + 5e3;
|
|
7658
8232
|
}
|
|
7659
|
-
await
|
|
8233
|
+
await delay4(Math.max(0, Math.min(DEFAULT_POLL_INTERVAL_MS, remaining)));
|
|
7660
8234
|
}
|
|
7661
8235
|
if (share.mode === "response" && stats.responseMatched === 0) {
|
|
7662
8236
|
logger15.warning(
|
|
@@ -7694,71 +8268,27 @@ var Share = {
|
|
|
7694
8268
|
* @returns {Promise<string>} base64 image
|
|
7695
8269
|
*/
|
|
7696
8270
|
async captureScreen(page, options = {}) {
|
|
7697
|
-
const originalViewport = await resolveCurrentViewportSize(page);
|
|
7698
|
-
const defaultBuffer = Math.round((originalViewport.height || 1080) / 2);
|
|
7699
|
-
const buffer = options.buffer ?? defaultBuffer;
|
|
7700
8271
|
const restore = options.restore ?? false;
|
|
7701
|
-
const maxHeight = options.maxHeight ?? 8e3;
|
|
7702
8272
|
const screenshotWatermarkify = resolveCaptureScreenWatermarkify(page, options.watermarkify);
|
|
7703
|
-
const compression =
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
7718
|
-
el.style.overflow = "visible";
|
|
7719
|
-
el.style.height = "auto";
|
|
7720
|
-
el.style.maxHeight = "none";
|
|
7721
|
-
}
|
|
7722
|
-
});
|
|
7723
|
-
return maxHeight2;
|
|
7724
|
-
});
|
|
7725
|
-
const targetHeight = Math.min(maxScrollHeight + buffer, maxHeight);
|
|
7726
|
-
await page.setViewportSize({
|
|
7727
|
-
width: originalViewport.width,
|
|
7728
|
-
height: targetHeight
|
|
7729
|
-
});
|
|
7730
|
-
await delay3(1e3);
|
|
7731
|
-
const capturedAt = /* @__PURE__ */ new Date();
|
|
7732
|
-
const rawBuffer = await capturePageScreenshot(page, {
|
|
7733
|
-
fullPage: true,
|
|
7734
|
-
type: "png",
|
|
7735
|
-
maxClipHeight: targetHeight
|
|
8273
|
+
const compression = resolveImageCompression(options);
|
|
8274
|
+
const captureOptions = {
|
|
8275
|
+
buffer: options.buffer ?? 0,
|
|
8276
|
+
restore,
|
|
8277
|
+
maxHeight: options.maxHeight,
|
|
8278
|
+
type: "png",
|
|
8279
|
+
visibleOnly: true
|
|
8280
|
+
};
|
|
8281
|
+
const capturedAt = /* @__PURE__ */ new Date();
|
|
8282
|
+
const rawBuffer = await captureExpandedFullPageScreenshot(page, captureOptions);
|
|
8283
|
+
let outputBuffer = rawBuffer;
|
|
8284
|
+
if (screenshotWatermarkify.enabled) {
|
|
8285
|
+
const watermarkifyMeta = await resolveScreenshotWatermarkifyMeta(page, {
|
|
8286
|
+
...screenshotWatermarkify,
|
|
8287
|
+
capturedAt
|
|
7736
8288
|
});
|
|
7737
|
-
|
|
7738
|
-
if (screenshotWatermarkify.enabled) {
|
|
7739
|
-
const watermarkifyMeta = await resolveScreenshotWatermarkifyMeta(page, {
|
|
7740
|
-
...screenshotWatermarkify,
|
|
7741
|
-
capturedAt
|
|
7742
|
-
});
|
|
7743
|
-
outputBuffer = await watermarkifyScreenshotBuffer(rawBuffer, watermarkifyMeta, page);
|
|
7744
|
-
}
|
|
7745
|
-
return await compressScreenshotBufferToBase64(outputBuffer, compression);
|
|
7746
|
-
} finally {
|
|
7747
|
-
if (restore) {
|
|
7748
|
-
await page.evaluate(() => {
|
|
7749
|
-
document.querySelectorAll(".__pk_expanded__").forEach((el) => {
|
|
7750
|
-
el.style.overflow = el.dataset.pkOrigOverflow || "";
|
|
7751
|
-
el.style.height = el.dataset.pkOrigHeight || "";
|
|
7752
|
-
el.style.maxHeight = el.dataset.pkOrigMaxHeight || "";
|
|
7753
|
-
delete el.dataset.pkOrigOverflow;
|
|
7754
|
-
delete el.dataset.pkOrigHeight;
|
|
7755
|
-
delete el.dataset.pkOrigMaxHeight;
|
|
7756
|
-
el.classList.remove("__pk_expanded__");
|
|
7757
|
-
});
|
|
7758
|
-
});
|
|
7759
|
-
await page.setViewportSize(originalViewport);
|
|
7760
|
-
}
|
|
8289
|
+
outputBuffer = await watermarkifyScreenshotBuffer(rawBuffer, watermarkifyMeta, page);
|
|
7761
8290
|
}
|
|
8291
|
+
return await compressImageBufferToBase64(outputBuffer, compression);
|
|
7762
8292
|
}
|
|
7763
8293
|
};
|
|
7764
8294
|
|